从左到右有几堆石子,双方轮流取石子,每次取时只能从最左边或者是最右边的堆中取任意个石子,不能操做的人算输,问先手有无必胜策略。html
首先定义dp状态:
表示在第
堆石子到第
堆石子左边放
个石子后是必败态。
表示在第
堆石子到第
堆石子左边放
个石子后是必败态。
两个值均可觉得0。
不难发现这两个值存在且惟一,最后只要判断
是否等于
便可。
下面考虑
的状态转移(
与它的转移方式相同):
递归边界:当
时,很显然,
首先求出
若
,则
而后能够发现,后手能够尽可能复制先手的操做,先手在左边取
个后,后手能够在右边也一样取
个,这样能够若是
,就能够保证先手先取完某一堆,这样只要保证先手取完某一堆后不是必败态就行,也就说若是先手取完第
堆,则要保证此时第
堆剩余的石子数不等于R,若是先手取完第
堆,则要保证此时第
堆剩余的石子数不等于L,所以还要加几个判断:
1.若
,则当先手在第
堆取了
个时,后手不能在第
堆取
个(不然先手能够直接取完第
堆的全部石子),所以
,这样当先手在左边取了
个或更多个石子后,后手只要在右边取
个石子便可。
2.若
,则当先手在第
堆取了
个时,后手一样不能复制先手的操做,所以
,这样当先手在右边取了
个或更多个石子后,后手只要在左边取
个石子便可。
3.若
,则
仍为
,理由与上面相同。ios
#include<iostream> #include<cstdio> #include<cstring> #define N 1010 using namespace std; int T,n,num[N],le[N][N],ri[N][N]; int askl(int u,int v); int askr(int u,int v) { if(u==v) return num[u]; if(ri[u][v]!=-1) return ri[u][v]; int L,R,res; L=askl(u+1,v); R=askr(u+1,v); if(num[u]==L) res=0; else res=num[u]-(num[u]>L)+(num[u]>=R); return ri[u][v]=res; } int askl(int u,int v) { if(u==v) return num[u]; if(le[u][v]!=-1) return le[u][v]; int L,R,res; L=askl(u,v-1); R=askr(u,v-1); if(num[v]==R) res=0; else res=num[v]-(num[v]>R)+(num[v]>=L); return le[u][v]=res; } int main() { int i,j; cin>>T; while(T--) { memset(le,-1,sizeof(le)); memset(ri,-1,sizeof(ri)); scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&num[i]); } printf("%d\n",num[1]!=askl(2,n)); } }