题目:看到 这里 的一个分类,打算分四次把它搞定吧,现在看第一部分的题:
//HDOJ1079 Calendar Game
具体情况具体分析就好,这题数据蛮弱,之前写错了也过了。
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
int t,y,m,d,tmp;
bool tag;
scanf("%d",&t);
while(t--)
{
tag=false;;
scanf("%d%d%d",&y,&m,&d);
tmp=m+d;
if(y==2001 && m==11 && d==4)
tag=false;
else if((m==1||m==3||m==5||m==7)&&d==31) // 8.31 10.31 和 12.31 果断抛弃
tag=true;
else if((m==4||m==6||m==9||m==11)&&d==30) //后续可选择为奇态
tag=true;
// 对于2.28的不管是不是闰年,后续可以是3.28为奇态
else if(tmp%2==0)
tag=true;
if(tag)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
//HDOJ1525&POJ2348 Euclid's Game
watashi翻译的那本书上有讲过,考虑一下没有选择的时候就快接近答案了。
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
bool win;
void solve(int a,int b)
{
if(a>b)
swap(a,b);
if(b%a==0 || b/a>1)
return ;
win=!win;
b=b%a;
solve(a,b);
}
int main()
{
int a,b;
while(scanf("%d%d",&a,&b),a+b)
{
win=1;
solve(a,b);
if(win)
printf("Stan wins\n");
else
printf("Ollie wins\n");
}
return 0;
}
//HDOJ1564 Play a game
水题
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int main()
{
int n;
while(scanf("%d",&n)!=EOF,n)
{
if(n&1)
printf("ailyanlu\n");
else
printf("8600\n");
}
return 0;
}
//HDOJ1846 Brave Game
简单巴什博弈
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
int n,m;
while(t--)
{
scanf("%d%d",&n,&m);
if(n%(m+1))
printf("first\n");
else
printf("second\n");
}
return 0;
}
//HDOJ1847 Good Luck in CET-4 Everybody!
枚举一下必败态和必胜态,或者用sg函数也可以解释
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
using namespace std;
set<int>s;
int main()
{
s.clear();
int two=1;
for(;two<=1000;two*=2)
s.insert(two);
for(int i=1;i<=1000;i++)
{
if(s.count(i))
continue;
for(set<int>::iterator it=s.begin();it!=s.end();it++)
{
if(*it+i<=1000)
s.insert(*it+i);
}
}
int n;
while(scanf("%d",&n)!=EOF)
{
if(s.count(n))
printf("Kiki\n");
else
printf("Cici\n");
}
return 0;
}
//HDOJ2147 kiki's game
跟圆桌那个差不多,取中心,然后跟着对手取对称的。
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
using namespace std;
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF,m+n)
{
if((n&1)&&(m&1))
printf("What a pity!\n");
else
printf("Wonderful!\n");
}
return 0;
}
//HDOJ2516
简单sg函数
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
using namespace std;
set<int>s;
int main()
{
s.clear();
int x=1,y=1,tmp=x+y;
while(tmp>0)
{
s.insert(tmp);
x=y;
y=tmp;
tmp=x+y;
}
int n;
while(scanf("%d",&n),n)
{
if(s.count(n))
printf("Second win\n");
else
printf("First win\n");
}
return 0;
}
//HDOJ2897
sg打表找规律,或者理解成巴什博弈
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
int main()
{
int n,p,q;
while(scanf("%d%d%d",&n,&p,&q)!=EOF)
{
int tmp=n%(p+q);
if(tmp==0 || tmp>p)
printf("WIN\n");
else
printf("LOST\n");
}
return 0;
}
//HDOJ3032 Nim or not Nim?
sg打表
// SG博弈
/*
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <set>
using namespace std;
#define maxn 2000
set<int>s;
int sg[maxn];
int solve()
{
int i=0;
while(s.count(i))
i++;
return i;
}
int main()
{
int n;
scanf("%d",&n);
sg[0]=0;
for(int i=0;i<=n;i++)
{
s.clear();
for(int j=0;j<i;j++)
{
s.insert(sg[j]);
s.insert(sg[j]^sg[i-j]);
}
sg[i]=solve();
}
for(int i=1;i<=n;i++)
cout<<i<<":"<<sg[i]<<endl;
}
*/
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
int sg(int n)
{
if(n%4==0)
return n-1;
if(n%4==3)
return n+1;
return n;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
int ans=0,x;
for(int i=0;i<n;i++)
{
scanf("%d",&x);
ans^=sg(x);
}
if(ans)
printf("Alice\n");
else
printf("Bob\n");
}
return 0;
}
//HDOJ3389 Game
1,3,4是不能够移动的终点。
// 打表找出最终不可移动的点
// 找出移动到不可移动点的编号的规律,转化成nim
/*
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <set>
using namespace std;
#define maxn 60
int st[maxn];
int main()
{
memset(st,0,sizeof(st));
for(int i=1;i<maxn;i++)
for(int j=i+1;j<maxn;j++)
if((i+j)%3==0 && ((i+j)/3)&1 )
st[j]=1;
printf("输出不能移动的位置:\n");
for(int i=1;i<maxn;i++)
if(st[i]==0) // 输出不能移动的位置
cout<<i<<" ";
puts("");
printf("可以移动的:\n");
for(int i=1;i<maxn;i++)
for(int j=i+1;j<maxn;j++)
if((i+j)%3==0 && ((i+j)/3)&1 )
cout<<i<<" <- "<<j<<endl;
return 0;
}
*/
// 发现对于能一次到达1,3,4状态的编号都是mod6的数
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
int n,x;
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(i%6==2 || i%6==0 || i%6==5)
ans^=x;
}
printf("Case %d: ",cas);
if(ans)
printf("Alice\n");
else
printf("Bob\n");
}
return 0;
}
//HDOJ3537 Daizhenyang's Coin
MT博弈
// Mock Turtles
// http://blog.sina.com.cn/s/blog_8f06da99010125ol.html
// http://blog.csdn.net/acm_cxlove/article/details/7854181
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <set>
using namespace std;
set<long long>s;
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
s.clear();
long long ans=0;
long long x;
for(int i=0;i<n;i++)
{
scanf("%I64d",&x);
s.insert(x);
}
set<long long>::iterator it=s.begin();
for(;it!=s.end();it++)
{
if(__builtin_popcount(2* *it)&1)
ans^=2* *it;
else
ans^=2* *it+1;
}
if(ans)
printf("No\n");
else
printf("Yes\n");
}
}
//HDOJ3544 Alice's Game
策略:每次尽量均分
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
int n;
scanf("%d",&n);
long long ansx=0,ansy=0,x,y;
while(n--)
{
scanf("%I64d%I64d",&x,&y);
while(x>1 && y>1)
{
x/=2;
y/=2;
}
if(x==1)
ansy+=y-1;
if(y==1)
ansx+=x-1;
}
printf("Case %d: ",cas);
if(ansx>ansy)
printf("Alice\n");
else
printf("Bob\n");
}
return 0;
}
//HDOJ3863 No Gambling
永远必胜
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{
int n;
while(scanf("%d",&n),n!=-1)
{
printf("I bet on Oregon Maple~\n");
}
return 0;
}
//HDOJ3951 Coin Game
一个硬币的时候判奇偶,两个及以上硬币的时候转化成跟watashi翻译那本书上那个博弈一样,第一次不管对手怎么拿,我们都可以转化成相同的两条链,然后跟对手一样对称的拿。
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int a[100];
int main()
{
int t;
int n,k;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
scanf("%d%d",&n,&k);
printf("Case %d: ",cas);
if(k==1)
{
if(n&1)
printf("first\n");
else
printf("second\n");
continue;
}
if(n-k<=0)
printf("first\n");
else
printf("second\n");
}
return 0;
}
//HDOJ2188 悼念512汶川大地震遇难同胞——选拔志愿者
简单巴什博弈
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
if(n%(m+1)==0)
printf("Rabbit\n");
else
printf("Grass\n");
}
return 0;
}
//HDOJ2149 Public Sale
巴什博弈第一步取法
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n%(m+1)==0)
printf("none\n");
else
{
if(n/(m+1)==0)
{
for(int i=n;i<m;i++)
printf("%d ",i);
printf("%d\n",m);
}
else
{
printf("%d\n",n%(m+1));
}
}
}
return 0;
}
//HDOJ1850 Being a Good Boy in Spring Festival
nim博弈取法
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int a[100];
int main()
{
int n,x;
while(scanf("%d",&n),n)
{
int ans=0,hah=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
ans^=a[i];
}
for(int i=0;i<n;i++)
{
if((ans^a[i])<a[i]) // 表明一开始选这个可以将ans二进制中某个1变成0
hah++;
}
printf("%d\n",hah);
}
return 0;
}
//HDOJ2176 取(m堆)石子游戏
nim博弈取法
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int a[200001];
int main()
{
int n;
while(scanf("%d",&n)!=EOF,n)
{
int ans=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
ans^=a[i];
}
if(ans==0)
{
printf("No\n");
continue;
}
printf("Yes\n");
for(int i=0;i<n;i++)
{
if((ans^a[i])<a[i])
printf("%d %d\n",a[i],ans^a[i]);
}
}
return 0;
}
//HDOJ1527&POJ1067 取石子游戏
简单威佐夫博弈
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
double tmp=(sqrt(5.0)+1)/2;
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n>m)
swap(n,m);
if(int((m-n)*tmp)==n)
printf("0\n");
else
printf("1\n");
}
return 0;
}
//HDOJ2177 取(2堆)石子游戏
威佐夫博弈第一步取法
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
double tmp=(sqrt(5.0)+1)/2;
map<int,int>mp;
map<int,int>ans;
int main()
{
mp.clear();
for(int i=0;i<1000010;i++)
mp[(int)i*tmp]=(int)i*tmp+i;
map<int,int>::iterator it=mp.begin();
int n,m;
while(scanf("%d%d",&n,&m)!=EOF,m+n)
{
ans.clear();
if(mp[n]==m)
printf("0\n");
else
{
printf("1\n");
map<int,int>::iterator it=mp.begin(),ii=mp.end(),jj=mp.end();
for(;it!=mp.end() && (it->first<=m);it++)
{
if(it->second==m)
{
ii=it;
}
else if(it->second==n)
{
jj=it;
}
if(n-it->first == m-it->second && m-it->second>0)
ans[it->first]=it->second;
}
for(it=ans.begin();it!=ans.end();it++)
{
printf("%d %d\n",it->first,it->second);
}
if(mp[n]&&mp[n]<m)
printf("%d %d\n",n,mp[n]);
if(ii!=mp.end()&&ii->first<n)
printf("%d %d\n",ii->first,m);
if(jj!=mp.end())
printf("%d %d\n",jj->first,jj->second);
}
}
return 0;
}
//HDOJ1517&POJ2505 A Multiplication Game
假设当前状态时必败态,然后一直往前推,能够到达必败态的是必胜态,只能到达必胜态的是必败态,枚举一下区间即可。
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{
long long n;
while(scanf("%I64d",&n)!=EOF)
{
bool ans=0;
while(n>1)
{
if(!ans)
n=(n-1)/9+1;
else
n=(n-1)/2+1;
ans^=1;
}
if(ans)
printf("Stan wins.\n");
else
printf("Ollie wins.\n");
}
return 0;
}
//HDOJ2486&HDOJ2580&POJ3922 A simple stone game
K倍动态减法博弈,具体证明看论文吧。
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
const int maxn = 1000010 ;
int a[maxn],b[maxn];
int n,k;
int solve(int n)
{
int i=0,j=0;
a[0]=1,b[0]=1;
while(a[i]<n)
{ // a[i]表示当前能用来构造的最大项
// b[i]表示由0...i,由a中数列所构成的最大值
i++;
a[i]=b[i-1]+1; // b[i-1]+1不能表示成a中的前i-1中某不连续几项的和,故需要构造
while(a[j+1]*k<a[i])
j++; // 找到最近的恰好与第i项差值在k倍以上的
if(a[j]*k<a[i])
b[i]=b[j]+a[i]; //用到了a中前i-2项,保证和为a中某不连续的话,可以取当前的j
else // 倒数第二项和最后一项差值恰好为k倍时,能构造的最大项不变
b[i]=a[i];
}
if(n==a[i])
return -1;
int ans;
while(n)
{
if(n>=a[i])
n-=a[i],ans=a[i];
i--;
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
scanf("%d%d",&n,&k);
int ans = solve(n);
printf("Case %d: ",cas);
if(ans==-1)
printf("lose\n");
else
printf("%d\n",ans);
}
return 0;
}
//HDOJ4315 Climbing the Hill
转化一下,类似于watashi翻译那本书上的一个nim博弈,好像正规叫法是阶梯博弈,特殊情况就是要讨论一下,k=1和k=2的情况,具体见代码
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int a[1010];
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)!=EOF)
{
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
if(k==1)
{
printf("Alice\n");
continue;
}
if(n&1)
{
ans^=a[1]-(k==2);
for(int i=2;i<n;i+=2)
ans^=(a[i+1]-a[i]-1);
}
else
{
for(int i=1;i<=n;i+=2)
ans^=(a[i+1]-a[i]-1);
}
if(ans)
printf("Alice\n");
else
printf("Bob\n");
}
return 0;
}
最后两道没来及学新知识,周末过了稍微闲点的时候再补上。
HDOJ1538 A Puzzle for Pirates [海盗分金问题]
HDOJ3404 Switch lights [Nim积]