Time Limit: 1000MS | Memory Limit: 32768KB | 64bit IO Format: %I64d & %I64u |
Description
Input
Output
Sample Input
2 2 5
3
2 5 12
3 2 4 7
4 2 3 7 12
5 1 2 3 4 5
3
2 5 12
3 2 4 7
4 2 3 7 12
0
Sample Output
Source
//f[]:可以取走的石子个数 //sg[]:0~n的SG函数值 //hash[]:mex{} int f[N],sg[N],hash[N]; void getSG(int n) { int i,j; memset(sg,0,sizeof(sg)); for(i=1;i<=n;i++) { memset(hash,0,sizeof(hash)); for(j=1;f[j]<=i;j++) hash[sg[i-f[j]]]=1; for(j=0;j<=n;j++) //求mes{}中未出现的最小的非负整数 { if(hash[j]==0) { sg[i]=j; break; } } } }
2dfs模板
dfs传入的参数x为每堆得数量,所以根据题意假如有n堆物品,那么就要进行n次传参就行dfs;
其实sg数组应该在主函数中进行初始化,
我们每次都dfs下一个状态,也就是当前的x状态经过f数组中其中一个取物品方法而得到的另外一种状态,队友回溯的时候我们找到没有被vis标记值
也就是mes集合里面后继点中未出现的最小的正整数值
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍 //n是集合s的大小 S[i]是定义的特殊取法规则的数组 int s[110],sg[10010],n; int SG_dfs(int x) { int i; if(sg[x]!=-1) return sg[x]; bool vis[110]; memset(vis,0,sizeof(vis)); for(i=0;i<n;i++) { if(x>=s[i]) { SG_dfs(x-s[i]); vis[sg[x-s[i]]]=1; } } int e; for(i=0;;i++) if(!vis[i]) { e=i; break; } return sg[x]=e; }
下面附上hdu1536的代码
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍 //n是集合s的大小 S[i]是定义的特殊取法规则的数组 int s[110],sg[10010],n; int SG_dfs(int x){ int i; if(sg[x]!=-1) return sg[x]; bool vis[110]; memset(vis,0,sizeof(vis)); for(i=0;i<n;i++){ if(x>=s[i]) { SG_dfs(x-s[i]); vis[sg[x-s[i]]]=1; } } int e; for(i=0;;i++) if(!vis[i]) { e=i; break; } return sg[x]=e; } int main() { int i,m,t,num; while(scanf("%d",&n)&&n) { for(i=0;i<n;i++) scanf("%d",&s[i]); memset(sg,-1,sizeof(sg)); sort(s,s+n); scanf("%d",&m); while(m--) { scanf("%d",&t); int ans=0; while(t--) { scanf("%d",&num); ans^=SG_dfs(num); } if(ans==0) printf("L"); else printf("W"); } printf("\n"); } return 0; }
下面同hdu3032
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 623 Accepted Submission(s): 288
思路: 此题为博弈中的—取走-分割游戏(这种游戏允许取走某些东西,然后将原来的一个游戏分成若干个相同的游戏)
由于数据范围,不能直接求sg值只能打表找规律;
Lasker's Nim游戏:每一轮允许两会中操作之一:①、从一堆石子中取走任意多个,②、将一堆数量不少于2的石子分成都不为空的两堆。
分析:很明显:sg(0) = 0,sg(1) = 1。
状态2的后继有:0,1和(1,1),他们的SG值分别为0,1,0,所以sg(2) =2。
状态3的后继有:0、1、2、(1,2),他们的SG值分别为0、1、2、3,所以sg(3) = 4。
状态4的后继有:0、1、2、3、(1,3)和(2,2),他们的SG值分别为0,1,2,4,5,0,所以sg(4) = 3.
再推一些,推测得到:对于所有的k >= 0,有 sg( 4k+1 ) = 4k+1; sg( 4k+2 ) = 4k+2; sg( 4k+3 ) = 4k+4; sg( 4k+4 ) = 4k+3。
假设游戏初始时有3堆,分别有2、5和7颗石子。三堆的SG函数值分别为2、5、8,他们的Nim和等于15.所以要走到P状态,就要使得第三堆的SG值变成7,可以将第三对按1和6分成两堆。
下面附上打表代码
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; const int N=1000010; int sg[N]; int g(int x){ int mex[1010]; memset(mex,0,sizeof(mex)); if(sg[x]!=-1) return sg[x]; for(int i=x-1;i>=0;i--)//在进入一种状态的时候,我们可以取走任意多的数量,但是不能不取,所以从x-1到0这些情况的mex【g(x)】都应该标记一下 mex[g(i)]=1; for(int i=1;i<=x/2;i++){ int ans=0; ans^=g(i);//同样一堆我们应该分成两堆,对这两队进行在一起dfs ans^=g(x-i); mex[ans]=1; } for(int i=0;;i++) if(!mex[i]) return sg[x]=i; } int main(){ //freopen("input.txt","r",stdin); int t,n; scanf("%d",&t); memset(sg,-1,sizeof(sg)); while(t--){ scanf("%d",&n); int x; for(int i=0;i<n;i++){ scanf("%d",&x); g(x); printf("sg[%d]=%d\n",x,sg[x]); } for(int i=0;i<=100;i++){ printf("%d ",sg[i]); //if(i%10==0) //system("pause"); } printf("\n"); } return 0; }
通过找规律我们可以总结出下面的代码
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; int main(){ int t; scanf("%d",&t); while(t--){ int ans=0; int n; scanf("%d",&n); int x; for(int i=0;i<n;i++){ scanf("%d",&x); if(x%4==1||x%4==2) ans^=x; else if(x%4==3) ans^=(x+1); else ans^=(x-1); } if(ans==0) printf("Bob\n"); else printf("Alice\n"); } return 0; }