bzoj1413 取石子游戏 递推

来源:互联网 发布:2017下半年软件设计师 编辑:程序博客网 时间:2024/05/18 02:27

       自古浙江出神题。。然后数据还很弱。。

       参考了这里的思路。可以发现对于任意一段[i,j],在其左边添上一个数,只有唯一的一个数(包括0即不添加)能够使新的序列[i-1,j]是一个必败状态。显然,如果有两个x,y都满足,不妨设x<y,那么对于y+[i,j]这个状态,可以把y取到x使其成为必败状态,这与每一个必败状态都转移不到必败状态矛盾。故得证。可知在右边添上一个数同理。

       令l[i][j]表示[i,j]左边添上的数,r[i][j]表示右边添上的数。假设我们已经知道了x=l[i-1][j],y=r[i][j-1],z=a[j],那么:

       1.特殊情况a[j]=y,那么[i,j]本身就是一个必败状态,l[i][j]=0;

       2.如果a[j]<x,y,那么令l[i][j]=a[j],然后先手在一边取k个,后手就在另一边取k个。新手显然先取到了,那么此时还剩下的那一堆的数量显然<x,y,因此后手有必胜策略;

       3.考虑x<=a[j]<y,那么令l[i][j]=a[j]+1,然后在第j堆个数>=x时,后手始终保持让第i-1堆得比第j堆得多一个;当第j堆个数<x时,后手始终保持第i-1堆和第j堆相同,然后同2;

       4.考虑y<a[j]<=x,那么令l[i][j]=a[j]-1,然后同3;

       5.考虑a[j]>x,y,那么令l[i][j]=a[j]。不妨设x<y(x>y同理),那么当第i-1堆个数>y时,后手保持第i-1堆和第j堆相同;然后同3;

       最后,如果a[1]==l[2][n]则无解;反之有解。

AC代码如下:

#include<iostream>#include<cstdio>#include<cstring>#define N 1005using namespace std;int n,a[N],l[N][N],r[N][N];int main(){int cas; scanf("%d",&cas);while (cas--){scanf("%d",&n); int i,j;for (i=1; i<=n; i++) scanf("%d",&a[i]);for (i=1; i<=n; i++) l[i][i]=r[i][i]=a[i];for (i=n-1; i; i--) for (j=i+1; j<=n; j++){int x=l[i][j-1],y=r[i][j-1],z=a[j];if (z==y) l[i][j]=0; elseif (z<x && z<y || z>x && z>y) l[i][j]=z; elseif (x>y) l[i][j]=z-1; else l[i][j]=z+1;x=r[i+1][j]; y=l[i+1][j]; z=a[i];if (z==y) r[i][j]=0; elseif (z<x && z<y || z>x && z>y) r[i][j]=z; elseif (x>y) r[i][j]=z-1; else r[i][j]=z+1;}if (n==1) puts("1"); else printf("%d\n",(a[1]==l[2][n])?0:1);}return 0;}


by lych
2016.2.24

0 0
原创粉丝点击