POJ 2960 S-Nim
大意:有n堆石子,每堆石子个数已知,两人轮流从中取石子,
每次可取的石子数x满足x属于集合S(k) = {s1,s2,s3...sk-1},问先拿者是否有必胜策略?
分析:
1.可将问题转化为n个子问题,每个子问题分别为:
从一堆x颗石子中取石子,每次可取的石子数为集合S(k)中的一个数
2.分析(1)中的每个子问题,
易得:SG(x) = mex(SG[x-s[i]])(0<i<k-1);
3.后面就是SG函数的应用,根据Sprague-Grundy Therem:g(G)=g(G1)^g(G2)^g(G3)^...^g(Gn)
即游戏的和的SG函数值是它的所有子游戏的SG函数值的异或,即
SG(G) = SG(x1)^SG(x2)^...^SG(xn),故若SG(G)=0那么必输
a27400 | 2960 | Accepted | 1596K | 407MS | G++ | 931B | 2011-08-27 19:26:24 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 10000
int s[110];
int k;
int sg[MAXN+10];
int SG(int a)
{
if(sg[a]!=-1)
return sg[a];
else
{
bool visit[MAXN+10]={0};
for(int i=1;i<=k;i++)
{
if(a>=s[i])
{
int temp=SG(a-s[i]);
visit[temp]=1;
}
}
for(int i=0;i<=MAXN;i++)
{
if(!visit[i])
{
return sg[a]=i;
}
}
}
return 0;
}
int main(void)
{
while(scanf("%d",&k)==1)
{
memset(s,0,sizeof(s));
memset(sg,-1,sizeof(sg));
if(k==0)
break;
int i;
for(i=1;i<=k;i++)
scanf("%d",&s[i]);
int T;
scanf("%d",&T);
while(T--)
{
int a;
scanf("%d",&a);
int ans=0;
for(i=1;i<=a;i++)
{
int st;
scanf("%d",&st);
st=SG(st);
ans^=st;
}
if(ans==0)
putchar('L');
else putchar('W');
}
puts("");
}
}
poj 1067 取石子游戏
典型威佐夫博弈
参考:http://hi.baidu.com/zhulei632/blog/item/657efefaf299b1dbb58f3152.html
a27400 | 1067 | Accepted | 392K | 0MS | G++ | 342B | 2011-08-28 00:54:15 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const double gold=(sqrt(5)+1)/2;
int main(void)
{
int a,b;
while(scanf("%d %d",&a,&b)==2)
{
if(b<a)
swap(a,b);
int temp=b-a;
if(floor((double)temp*gold)==a)
puts("0");
else puts("1");
}
}
poj 1082 Calendar Game
这个题分析挺麻烦的……反正我是看题解才过去的
http://www.cnblogs.com/ybrbupt/archive/2011/08/28/2155952.html
a27400 | 1082 | Accepted | 388K | 0MS | G++ | 357B | 2011-08-28 09:39:32 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int main(void)
{
int T;
scanf("%d",&T);
while(T--)
{
int year,month,day;
scanf("%d %d %d",&year,&month,&day);
if((month+day)%2==0||(month==9&&day==30)||(month==11&&day==30))
puts("YES");
else puts("NO");
}
}
poj 1740 A New Stone Game
楼教的男人八题,一不小心做了一道,原来也不是那么可怕嘛~~
分析: http://www.cnblogs.com/ybrbupt/archive/2011/08/28/2155973.html
a27400 | 1740 | Accepted | 388K | 16MS | G++ | 465B | 2011-08-28 10:43:45 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
int stone[110];
int main(void)
{
int n;
while(scanf("%d",&n)==1)
{
if(!n)
break;
memset(stone,0,sizeof(stone));
int i;
for(i=1;i<=n;i++)
{
int a;
scanf("%d",&a);
stone[a]++;
}
int flag=0;
for(i=1;i<=100;i++)
{
if(stone[i]%2==1)
flag=1;
}
if(flag)
puts("1");
else puts("0");
}
return 0;
}
poj 2234 Matches Game
红果果的NIM啊~~~小心大小写就可以~~
a27400 | 2234 | Accepted | 388K | 16MS | G++ | 311B | 2011-08-28 22:50:16 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
int main(void)
{
int n;
while(scanf("%d",&n)==1)
{
int i;
int ans=0;
for(i=1;i<=n;i++)
{
int a;
scanf("%d",&a);
ans^=a;
}
if(ans==0)
puts("No");
else puts("Yes");
}
}
poj 2348 Euclid's Game
很好的一道分析推理的博弈
对于任意一个局面(a,b),它是必胜局还是必败局这是确定的。
对于局面(m,n)(m>n),两人一直往下取,必然会到局面(m%n,n)。
如果m/n<2,则此时只有一种往下走的方法,下一步必然是(m%n,n)。
如果m/n>2,则局面(m,n)的先取者就可以决定由谁去面对局面(m%n,n),因为这个先取者足够聪明可以判断(m%n,n)是必胜还是必败,因此我们也已经可以确定(m,n)的先取者已经胜了。
a27400 | 2348 | Accepted | 388K | 0MS | G++ | 494B | 2011-08-28 23:49:34 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int stone(int n,int m,int win)
{
if(n%m==0)
return !win;
else if(n/m>=2)
{
return !win;
}
else return stone(m,n%m,!win);
}
int main(void)
{
int n,m;
while(scanf("%d %d",&n,&m)==2)
{
if(!(n+m))
break;
int flag=0;
if(n<m)
swap(n,m);
flag=stone(n,m,0);
if(flag==1)
puts("Stan wins");
else puts("Ollie wins");
}
}
poj 2311 Cutting Game
很典型的SG函数博弈,注意子游戏和后继的区别就好~~
a27400 | 2311 | Accepted | 560K | 79MS | G++ | 707B | 2011-08-29 01:13:32 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
int sg[210][210];
int dfs(int n,int m)
{
if(sg[n][m]!=-1)
return sg[n][m];
int visit[210]={0};
int i;
for(i=2;i<=n/2;i++)
{
int u=dfs(i,m)^dfs(n-i,m);
visit[u]=1;
}
for(i=2;i<=m/2;i++)
{
int u=dfs(n,i)^dfs(n,m-i);
visit[u]=1;
}
for(i=0;visit[i];i++);
return sg[n][m]=sg[m][n]=i;
}
int main(void)
{
memset(sg,-1,sizeof(sg));
int n,m;
while(scanf("%d %d",&n,&m)==2)
{
if(dfs(n,m)!=0)
puts("WIN");
else puts("LOSE");
// printf("%d\n",sg[1][3]);
}
return 0;
}
poj 2505 A multiplication Game
确定必胜区间和必败区间,简单
a27400 | 2505 | Accepted | 404K | 0MS | G++ | 295B | 2011-08-30 10:26:58 |
#include<stdio.h>
int main()
{
double n;
while(scanf("%lf",&n)!=EOF)
{
while(n>18)
n=n/18;
if(n<=9)
printf("Stan wins.\n");
else
printf("Ollie wins.\n");
}
return 0;
}
poj 2425 A Chess Game
裸SG函数,图都帮你建好了,当练手用把~~
a27400 | 2425 | Accepted | 4696K | 407MS | G++ | 941B | 2011-08-30 20:21:44 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
int map[1010][1010];
int sg[1010];
int dfs(int v)
{
if(sg[v]!=-1)
return sg[v];
bool visit[1010];
memset(visit,0,sizeof(visit));
int i;
for(i=1;i<=map[v][0];i++)
{
int u=dfs(map[v][i]);
visit[u]=1;
}
for(i=0;visit[i];i++);
return sg[v]=i;
}
int main(void)
{
int n;
while(scanf("%d",&n)!=EOF)
{
memset(map,0,sizeof(map));
memset(sg,-1,sizeof(sg));
int i;
for(i=0;i<n;i++)
{
int a;
scanf("%d",&a);
map[i][0]=a;
int j;
for(j=1;j<=a;j++)
{
scanf("%d",&map[i][j]);
}
}
int q;
while(scanf("%d",&q)==1)
{
if(!q)
break;
int j;
int ans=0;
for(j=0;j<q;j++)
{
int v;
scanf("%d",&v);
ans^=dfs(v);
}
if(ans==0)
puts("LOSE");
else puts("WIN");
}
}
}
hdu 3980 Paint Chain
SG函数,和poj2311非常相似。。
当时多校的时候还不会SG函数,现在随便敲敲就过了,挺有成就感的……
2011-08-29 10:27:42 | Accepted | 3980 | 140MS | 724K | 792 B | G++ | Tiramitu |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
int sg[1010][1010];
int dfs(int n,int m)
{
// printf("n:%d m:%d\n",n,m);
bool visit[1010]={0};
if(sg[n][m]!=-1)
return sg[n][m];
int i;
for(i=0;i<=(n-m)/2;i++)
{
if(n<m) break;
int u=dfs(i,m)^dfs(n-m-i,m);
visit[u]=1;
}
for(i=0;visit[i];i++);
return sg[n][m]=i;
}
int main(void)
{
int T;
int cas=0;
scanf("%d",&T);
memset(sg,-1,sizeof(sg));
while(T--)
{
int n,m;
int flag=1;
scanf("%d %d",&n,&m);
if(n==m)
flag=1;
else if(n<m)
flag=2;
else
{
int temp=dfs(n-m,m);
if(temp==0)
flag=1;
else flag=2;
}
if(flag==1)
printf("Case #%d: aekdycoin\n",++cas);
else printf("Case #%d: abcdxyzk\n",++cas);
}
}