取石子游戏之尼姆博弈
2012-07-23 08:43
351 查看
尼姆博弈:有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况与二进制有着很大的关系,我们用(a,b,c)来表示某种局势,那么(0,0,0)必然为奇异局势,最后一个面对这个局势的必败。(0,n,n)也是种奇异局势。因为如果对手在其中一堆取m个石子(m<=n),那么你也可以在另外一堆中取m个,他取几个你就取几个,到最后有一堆变为0的时候,你再取完另一堆胜利。
直接说结论吧:
对于任意的奇异局势(a,b,c),都有a^b^c=0。(^为异或运算)。
对于任意的非奇异局势(a,b,c),假设a<b<c,将它变为奇异局势的方法是:将c变成a^b。
原理:因为a^a=0,所以a^b^c=a^b^(a^b)=(a^a)^(b^b)=0,所以只需将c-(a^b)即可。
例1:有个非奇异局势(14,21,39),因为14^21=27,39-27=12,所以从39中拿走12个物体即可达到奇异局势(14,21,27)。
例2:我们来实际进行一盘比赛看看:
甲:(7,8,9)->(1,8,9)奇异局势
乙:(1,8,9)->(1,8,4)
甲:(1,8,4)->(1,5,4)奇异局势
乙:(1,5,4)->(1,4,4)
甲:(1,4,4)->(0,4,4)奇异局势
乙:(0,4,4)->(0,4,2)
甲:(0.4,2)->(0,2,2)奇异局势
乙:(0,2,2)->(0,2,1)
甲:(0,2,1)->(0,1,1)奇异局势
乙:(0,1,1)->(0,1,0)
甲:(0,1,0)->(0,0,0)奇异局势
甲胜。
性质1:对于某个局面(a1,a2,...,an),若a1^a2^...^an!=0,一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。
分析:令res=a1^a2^......an,设res的最高位为pos(那pos的数肯定为1啦),那么一定存在某个ai,它的二进制在pos位上也是1,(否则k的最高位那个1是怎么得到的)。异或res后这位变为0,这时ai^res<ai一定成立。则我们可以将ai改变成ai'=ai^res,此时a1^a2^...^ai'^...^an=a1^a2^...^an^res=0。
性质2:对于某个局面(a1,a2,...,an),若a1^a2^...^an=0,一定不存在某个合法的移动,将ai改变成ai'后仍满足a1^a2^...^ai'^...^an=0。
例题1:POJ 2975,题目意思是给你一组局势,问有多少中必胜的策略。
分析:求出res=a[0]^a[1]^a[2].....a[n-1],由于每次只能改变一堆石子的数量,由性质1,可知如果res!=0,那么一定存在某个合法的操作,使得res=0,只要满足a[i]*res<=a[i]。
SG函数:定义mex运算为最小不属于这个集合的非负整数。mex{0,1,2,4}=3,mex{}=0。
对于一个给定的有向无环图,我们定义关于图的每个顶点的SG函数g如下:g(x)=mex{g(y) | y是x的后继}。
SG函数的性质:由于是一个有向无环图,那么对于所有的末端位置,由于没有后继,所以SG=0。另外对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0(由于集合中的元素是不可以重复的,所以后面的g(y)!=0)。对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0(这个时候最小的非负整数一定是0)。
由上可知:顶点x所代表的位置是必败点条件是:g(x)=0。
看到一个经典的话额,就贴在这里吧:
有些事,明知是错的,也要去坚持,因为不甘心;有些人,明知是爱的,也要去放弃,因为没结局;有时候,明知没路了,却还在前行,因为习惯了。
POJ 2488~八嘎
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int MAX=10;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int visit[MAX][MAX];
int n,m,dx[8]={-2,-2,-1,-1,1,1,2,2},dy[8]={-1,1,-2,2,-2,2,-1,1};
struct Point
{ int x,y;
};
Point Start;
vector<Point> V;
bool Inside(Point P)
{ return P.x>=1&&P.x<=m&&P.y>=1&&P.y<=n;
}
void DFS(Point P)
{ Point s;
for(int i=0;i<8;i++)
{ s.x=P.x+dx[i];
s.y=P.y+dy[i];
if(Inside(s)&&!visit[s.x][s.y])
{ visit[s.x][s.y]=1;
V.push_back(s);
DFS(s);
}
}
}
int main()
{ int Case,num=1;
cin>>Case;
while(Case--)
{ cin>>n>>m;
CLR(visit,0);
cout<<"Scenario #"<<num++<<":"<<endl;
Start.x=1,Start.y=1;
visit[1][1]=1;
V.push_back(Start);
DFS(Start);
if(V.size()==n*m)
{ for(vector<Point>::size_type i=0;i<V.size();i++)
cout<<char(V[i].x+'A'-1)<<V[i].y;
cout<<endl;
}
else cout<<"impossible"<<endl;
V.clear();
if(Case) cout<<endl;
}
return 0;
}
这种情况与二进制有着很大的关系,我们用(a,b,c)来表示某种局势,那么(0,0,0)必然为奇异局势,最后一个面对这个局势的必败。(0,n,n)也是种奇异局势。因为如果对手在其中一堆取m个石子(m<=n),那么你也可以在另外一堆中取m个,他取几个你就取几个,到最后有一堆变为0的时候,你再取完另一堆胜利。
直接说结论吧:
对于任意的奇异局势(a,b,c),都有a^b^c=0。(^为异或运算)。
对于任意的非奇异局势(a,b,c),假设a<b<c,将它变为奇异局势的方法是:将c变成a^b。
原理:因为a^a=0,所以a^b^c=a^b^(a^b)=(a^a)^(b^b)=0,所以只需将c-(a^b)即可。
例1:有个非奇异局势(14,21,39),因为14^21=27,39-27=12,所以从39中拿走12个物体即可达到奇异局势(14,21,27)。
例2:我们来实际进行一盘比赛看看:
甲:(7,8,9)->(1,8,9)奇异局势
乙:(1,8,9)->(1,8,4)
甲:(1,8,4)->(1,5,4)奇异局势
乙:(1,5,4)->(1,4,4)
甲:(1,4,4)->(0,4,4)奇异局势
乙:(0,4,4)->(0,4,2)
甲:(0.4,2)->(0,2,2)奇异局势
乙:(0,2,2)->(0,2,1)
甲:(0,2,1)->(0,1,1)奇异局势
乙:(0,1,1)->(0,1,0)
甲:(0,1,0)->(0,0,0)奇异局势
甲胜。
性质1:对于某个局面(a1,a2,...,an),若a1^a2^...^an!=0,一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。
分析:令res=a1^a2^......an,设res的最高位为pos(那pos的数肯定为1啦),那么一定存在某个ai,它的二进制在pos位上也是1,(否则k的最高位那个1是怎么得到的)。异或res后这位变为0,这时ai^res<ai一定成立。则我们可以将ai改变成ai'=ai^res,此时a1^a2^...^ai'^...^an=a1^a2^...^an^res=0。
性质2:对于某个局面(a1,a2,...,an),若a1^a2^...^an=0,一定不存在某个合法的移动,将ai改变成ai'后仍满足a1^a2^...^ai'^...^an=0。
例题1:POJ 2975,题目意思是给你一组局势,问有多少中必胜的策略。
分析:求出res=a[0]^a[1]^a[2].....a[n-1],由于每次只能改变一堆石子的数量,由性质1,可知如果res!=0,那么一定存在某个合法的操作,使得res=0,只要满足a[i]*res<=a[i]。
#include<iostream> using namespace std; const int MAX=1010; int a[MAX]; int main() { int n,res; while(cin>>n,n) { int count=0; for(int i=0;i<n;i++) cin>>a[i]; res=a[0]; for(int i=1;i<n;i++) res^=a[i]; if(res==0) cout<<0<<endl; else { for(int i=0;i<n;i++) if((a[i]^res)<a[i]) count++; //要注意^和<=的优先级,必须加括号 cout<<count<<endl; } } return 0; }
SG函数:定义mex运算为最小不属于这个集合的非负整数。mex{0,1,2,4}=3,mex{}=0。
对于一个给定的有向无环图,我们定义关于图的每个顶点的SG函数g如下:g(x)=mex{g(y) | y是x的后继}。
SG函数的性质:由于是一个有向无环图,那么对于所有的末端位置,由于没有后继,所以SG=0。另外对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0(由于集合中的元素是不可以重复的,所以后面的g(y)!=0)。对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0(这个时候最小的非负整数一定是0)。
由上可知:顶点x所代表的位置是必败点条件是:g(x)=0。
看到一个经典的话额,就贴在这里吧:
有些事,明知是错的,也要去坚持,因为不甘心;有些人,明知是爱的,也要去放弃,因为没结局;有时候,明知没路了,却还在前行,因为习惯了。
POJ 2488~八嘎
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int MAX=10;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int visit[MAX][MAX];
int n,m,dx[8]={-2,-2,-1,-1,1,1,2,2},dy[8]={-1,1,-2,2,-2,2,-1,1};
struct Point
{ int x,y;
};
Point Start;
vector<Point> V;
bool Inside(Point P)
{ return P.x>=1&&P.x<=m&&P.y>=1&&P.y<=n;
}
void DFS(Point P)
{ Point s;
for(int i=0;i<8;i++)
{ s.x=P.x+dx[i];
s.y=P.y+dy[i];
if(Inside(s)&&!visit[s.x][s.y])
{ visit[s.x][s.y]=1;
V.push_back(s);
DFS(s);
}
}
}
int main()
{ int Case,num=1;
cin>>Case;
while(Case--)
{ cin>>n>>m;
CLR(visit,0);
cout<<"Scenario #"<<num++<<":"<<endl;
Start.x=1,Start.y=1;
visit[1][1]=1;
V.push_back(Start);
DFS(Start);
if(V.size()==n*m)
{ for(vector<Point>::size_type i=0;i<V.size();i++)
cout<<char(V[i].x+'A'-1)<<V[i].y;
cout<<endl;
}
else cout<<"impossible"<<endl;
V.clear();
if(Case) cout<<endl;
}
return 0;
}
相关文章推荐
- HDOJ2176取(m堆)石子游戏尼姆博弈 -输出取法
- ACM-尼姆博弈之取(m堆)石子游戏——hdu2176
- hdu-2176-取(m堆)石子游戏-尼姆博弈 -输出取法
- ACM-尼姆博弈之取(m堆)石子游戏——hdu2176
- ACM HDU 2516 取石子游戏(博弈)
- hdu2516-取石子游戏 (斐波那契博弈)【博弈 二分查找】
- POJ.1067 取石子游戏 (博弈论 威佐夫博弈)
- POJ 1067 取石子游戏 威佐夫博弈
- HDU 1527 取石子游戏 (威佐夫博弈)
- hdu 2516 取石子游戏 博弈 斐波那契数
- HDU 1527 取石子游戏(威佐夫博弈)
- HDOJ 题目2076取(m堆)石子游戏(nim博弈)
- HDU 2176 取(m堆)石子游戏(博弈)
- 取石子游戏(博弈算法)
- {HDU}{2516}{取石子游戏}{斐波那契博弈}
- poj 1067 取石子游戏(威佐夫博弈)
- Poj 1067 取石子游戏 (博弈)
- 2516取石子游戏(简单博弈)
- 【博弈-找规律】HDOJ 取石子游戏 2516
- 【博弈】取石子游戏