poj博弈n连刷

近期一直刷博弈。

简单做个总结。

接下来的题目,不按难度排序。

1,poj 1082    题目大意:1900-1-1至2001-11-4这段时间内选一天,加入是 y-m-d,就是y年m月d日,然后开始玩游戏罗。
 A和B轮流,每个人可以选择下一天,或者是下个月的这一天。但是最后一天都是2001-11-4.谁无法操作谁输哦。
注:要考虑闰年。

可以按常规来做,但我不是很想打,于是上网查了查有没有什么高级方法。

还真有,从后往前推我们可以看出,11月4日的月份和日期的和为奇数,是必败态,那么它的前一天,上个月的这一天,是必胜态,月份和日期的和为偶数。如果继续推的话,可以发现,月份和日期的和为奇数的话必败,除了9月30日和11月30日。月份和日期的和为偶数的话必胜。

代码如下:

#include<cstdio>
#include<cstdlib>
#include<cstring>
int t;
bool check(int y,int z)
{
	if((y+z)%2==0) return true;
	if(z==30&&(y==9||y==11)) return true;
	return false;
}
int main()
{
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		if(check(y,z)==true) printf("YES\n");
		else printf("NO\n");
	}
}



2:poj2960:有k个数,有n堆石头,每次只能在其中一堆石头中拿石子(至少拿一个),每次拿的石子的数目是k个数里的其中一个。

基础NIM。

如果石头数是k的话,为必胜。

从前往后推sg数组。

代码如下:

#include<cstdio>
#include<cstdlib>
#include<cstring>
const int N=9000;
int k,t,n;
int sa[N];
int sg[N+2100];
bool tf[N];
int getsg(int x)
{
	memset(tf,false,sizeof(tf));
	for(int i=1;i<=k;i++)
	if(x-sa[i]>=0)
	tf[sg[x-sa[i]]]=true;
	int t=0;
	while(tf[t]==true) t++;
	return t;
}
int main()
{
	while(scanf("%d",&k)!=EOF)
	{
		if(k==0) break;
		for(int i=1;i<=k;i++)
		scanf("%d",&sa[i]);
		for(int i=1;i<=10100;i++)
		sg[i]=getsg(i);
		scanf("%d",&t);
		for(int i=1;i<=t;i++)
		{
			scanf("%d",&n);
			int ans=0;
			for(int j=1;j<=n;j++)
			{
				int x;
				scanf("%d",&x);
				ans=ans^sg[x];
			}
			if(ans==0) printf("L");
			else printf("W");
		}
		printf("\n");
	}
}

poj2311:题目大意: 给出一个N*M的纸片,每一次可以把一部分剪成两部分,谁剪出1*1的就赢了。

构造sg+乱搞。。。

#include<cstdio>
#include<cstdlib>
#include<cstring>
const int N=300;

int n,m;
int sg[N][N];
bool tf[2000];
int dfs(int x,int y)
{
	if(sg[x][y]!=-1)
	return sg[x][y];
	for(int i=2;i<=x-i;i++)
	tf[dfs(i,y)^dfs(x-i,y)]=true;
	for(int i=2;i<=y-i;i++)
	tf[dfs(x,i)^dfs(x,y-i)]=true;
	int t=0;
	while(tf[t]==true) t++;
	sg[x][y]=t;
	return t;
}
int main()
{
	memset(sg,-1,sizeof(sg));
	for(int i=1;i<=204;i++)
	{
		sg[1][i]=0;
		sg[i][1]=0;
	}
	sg[2][2]=0;
	sg[2][3]=0;
	sg[3][2]=0;
	while(scanf("%d %d",&n,&m)!=EOF)
	{
		memset(tf,false,sizeof(tf));
		if(dfs(n,m)==0) printf("LOSE\n");
		else printf("WIN\n");
	}
}

poj2425:

有一个N个点的有向拓扑图,上面有M个旗子,棋子放在一个点上,一个点上可以放多个棋子。每次可以选一个棋子沿着一条有向边走一步(走到相邻的棋子上)。最后无法走棋的人输。
先手赢输出“WIN”
否则输出”LOSE“

还是构造SG

自己看代码吧。

#include<cstdio>
#include<cstdlib>
#include<cstring>
const int N=1100;
struct node{
	int x,y,next;
}sa[N*1000];int len,first[N];
void ins(int x,int y)
{
	len++;
	sa[len].x=x;
	sa[len].y=y;
	sa[len].next=first[x];
	first[x]=len;
}
int sg[N],ss[N],h[N];
bool tf[N];
int getsg(int x)
{
	memset(tf,false,sizeof(tf));
	for(int i=first[x];i!=-1;i=sa[i].next)
	{
		int y=sa[i].y;
		tf[sg[y]]=true;
	}
	int t=0;
	while(tf[t]==true) t++;
	return t;
}
int n,m;
int main()
{
	while(scanf("%d",&n)!=EOF)
	{
		len=0;
		memset(first,-1,sizeof(first));
		memset(ss,0,sizeof(ss));
		for(int i=0;i<n;i++)
		{
			int x,y;
			scanf("%d",&x);
			if(x==0) sg[i]=0;
			for(int j=1;j<=x;j++)
			{
				scanf("%d",&y);
				ss[y]++;
				ins(i,y);
			}
		}
		int st=1,ed=1;
		for(int i=0;i<n;i++) if(ss[i]==0) {h[ed++]=i;ss[i]=99999999;}
		while(st!=ed)
		{
			int x=h[st];
			for(int i=first[x];i!=-1;i=sa[i].next)
			{
				int y=sa[i].y;
				ss[y]--;
				if(ss[y]==0) 
				{
					h[ed++]=y;
					ss[y]=99999999;
				}
			}
			st++;
		}
		for(int i=ed-1;i>=1;i--) sg[h[i]]=getsg(h[i]); 
		while(1)
		{
			int x;
			scanf("%d",&m);
			int ans=0;
			if(m==0) break;
			for(int i=1;i<=m;i++)
			{
				scanf("%d",&x);
				ans=ans^sg[x];
			}
			if(ans==0) printf("LOSE\n");
			else printf("WIN\n");
		}
	}
}

没什么耐心打下去了。

有空在补。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值