8.16 训练13 SG函数 博弈和问题

本文深入探讨了多种博弈论游戏的算法实现,包括S-Nim、StoneGame、Fibonacci再次来袭以及Nim or not Nim?等游戏。通过使用SG函数,文章详细解释了如何在限制条件下确定游戏的最佳策略,提供了代码示例并分析了时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

S-Nim

Stone Game

Fibonacci again and again

Nim or not Nim?


今天本来做的挺快的,结果被最后一题卡了,又没有早走呜呜。早晚有一天我的脑子不会瓦特,代码不会这么蠢
另外今天有老师家的嘎嘣豆子,吵的很!!


S-Nim

Problem Description
Arthur and his sister Caroll have been playing a game called Nim for some time now. Nim is played as follows:

The starting position has a number of heaps, all containing some, not necessarily equal, number of beads.

The players take turns chosing a heap and removing a positive number of beads from it.

The first player not able to make a move, loses.

Arthur and Caroll really enjoyed playing this simple game until they recently learned an easy way to always be able to find the best move:

Xor the number of beads in the heaps in the current position (i.e. if we have 2, 4 and 7 the xor-sum will be 1 as 2 xor 4 xor 7 = 1).

If the xor-sum is 0, too bad, you will lose.

Otherwise, move such that the xor-sum becomes 0. This is always possible.

It is quite easy to convince oneself that this works. Consider these facts:

The player that takes the last bead wins.

After the winning player’s last move the xor-sum will be 0.

The xor-sum will change after every move.

Which means that if you make sure that the xor-sum always is 0 when you have made your move, your opponent will never be able to win, and, thus, you will win.

Understandibly it is no fun to play a game when both players know how to play perfectly (ignorance is bliss). Fourtunately, Arthur and Caroll soon came up with a similar game, S-Nim, that seemed to solve this problem. Each player is now only allowed to remove a number of beads in some predefined set S, e.g. if we have S =(2, 5) each player is only allowed to remove 2 or 5 beads. Now it is not always possible to make the xor-sum 0 and, thus, the strategy above is useless. Or is it?

your job is to write a program that determines if a position of S-Nim is a losing or a winning position. A position is a winning position if there is at least one move to a losing position. A position is a losing position if there are no moves to a losing position. This means, as expected, that a position with no legal moves is a losing position.
Input
Input consists of a number of test cases. For each test case: The first line contains a number k (0 < k ≤ 100 describing the size of S, followed by k numbers si (0 < si ≤ 10000) describing S. The second line contains a number m (0 < m ≤ 100) describing the number of positions to evaluate. The next m lines each contain a number l (0 < l ≤ 100) describing the number of heaps and l numbers hi (0 ≤ hi ≤ 10000) describing the number of beads in the heaps. The last test case is followed by a 0 on a line of its own.
Output
For each position: If the described position is a winning position print a ‘W’.If the described position is a losing position print an ‘L’. Print a newline after each test case.
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
问题大意这个题就是Nim游戏的变形,看标题就知道。就是让你只能取特定数目的石子,最后取的赢
今天的题目都是博弈和问题,废话少说,用SG函数。
SG(x)=mex{ SG(x-s[i]) }
算是个小模板吧,a数组记录后继元素。mex函数和a数组可以不要。

//You has the final say in what kind of life you want.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int s[150],k;
int heap[150],q;
bool flag[10000+10];
int sg[10000+10];
int a[10000+10],n;
int mex()
{
	memset(flag,0,sizeof(flag));
	sort(a,a+n);
	for(int i=0;i<n;i++)
	{
		flag[a[i]]=1;
	}
	for(int i=0;i<n+1;i++)
		if(!flag[i])
			return i;
//	cout<<"ERROR"<<endl;
	return 0;
}

void SG(int x)
{
	a[0]=0;
	n=0;
	for(int i=0;i<k;i++)
	{
		if(x<s[i]) break;
		a[n++]=sg[x-s[i]];
	}	
	sg[x]=mex();
	return;
}
int main()
{
	int m;
	int ans;
	while(1)
	{
		scanf("%d",&k);
		if(k==0)
			break;
		for(int i=0;i<k;i++)
			scanf("%d",s+i);
		sort(s,s+k);
		memset(sg,0,sizeof(sg));
		for(int i=0;i<=10000;i++)
			SG(i);
		
	//	for(int i=0;i<13;i++)
	//		cout<<sg[i]<<" ";
		
		scanf("%d",&m);
		while(m--)
		{
			scanf("%d",&q);
			for(int i=0;i<q;i++)
				scanf("%d",heap+i);
			ans=sg[heap[0]];
			for(int i=1;i<q;i++)
				ans=ans^sg[heap[i]];
			if(ans)
				printf("W");
			else printf("L");
		}
		printf("\n");
		
	}

	return 0;
}

Stone Game

Problem Description
This game is a two-player game and is played as follows:

  1. There are n boxes; each box has its size. The box can hold up to s stones if the size is s.
  2. At the beginning of the game, there are some stones in these boxes.
  3. The players take turns choosing a box and put a number of stones into the box. The number mustn’t be great than the square of the number of stones before the player adds the stones. For example, the player can add 1 to 9 stones if there are 3 stones in the box. Of course, the total number of stones mustn’t be great than the size of the box.
    4.Who can’t add stones any more will loss the game.

Give an Initial state of the game. You are supposed to find whether the first player will win the game if both of the players make the best strategy.
Input
The input file contains several test cases.
Each test case begins with an integer N, 0 < N ≤ 50, the number of the boxes.
In the next N line there are two integer si, ci (0 ≤ ci ≤ si ≤ 1,000,000) on each line, as the size of the box is si and there are ci stones in the box.
N = 0 indicates the end of input and should not be processed.
Output
For each test case, output the number of the case on the first line, then output “Yes” (without quotes) on the next line if the first player can win the game, otherwise output “No”.
Sample Input
3
2 0
3 3
6 2
2
6 3
6 3
0
Sample Output
Case 1:
Yes
Case 2:
No
问题大意有n个书包,每个的容量为si,已经装了ci。然后两个无聊的人往书包里装东西,只能装1~ci^2个,而且不能超过容量,最后装的人赢。
这个题挺有意思的。依然用SG函数做。
SG(x)=mex{SG(x+1),SG(x+2)……SG(x+x^2)} (x+x^2<=s)
之后一提交发现超时了,本小傻子忘了算时间复杂度了。居然达到了10^12.
那优化一下吧,因为到1000时,x+x^2 >10^6,1000以后的直接标出来吧,发现还是不行。
再后来开窍,从后往前扫,当出现第一个x+x^2<s的时候,它的SG肯定是0,之后x-1的SG值肯定是1,可以证明 x+x^ 2-(x-1)-(x-1)^2>1。之后就又是一通直接编号,到第一个够不到x的y,给y的sg写0,再编号。这样直接编到头都没问题,复杂度O(n)

//You has the final say in what kind of life you want.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 1000000+10
using namespace std;
int sg[MAX];
int SG(int c,int s)
{
	memset(sg,0,sizeof(sg));
	sg[s]=0;
	int minn;
	for(int i=s-1;i>=c;i--)
	{
		if(i>1000||i+i*i>=s)
			sg[i]=s-i;
		else
		{
			sg[i]=0;
			s=i;
		}
			
	}
//	cout<<"SG: "<<c<<","<<s<<":"<<endl;
//	for(int i=0;i<10;i++)                      
//		cout<<sg[i]<<" ";
//	cout<<endl;
	return sg[c];
}

int main()
{
	int N;
	int ans;
	int c,s;
	int cnt=1;
	while(1)
	{
		scanf("%d",&N);
		if(!N)break;
		scanf("%d%d",&s,&c);
		ans=SG(c,s);
		for(int i=1;i<N;i++)
		{
			scanf("%d%d",&s,&c);
			ans=ans^SG(c,s);
		}		
		printf("Case %d:\n",cnt++);
		if(ans)
			printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

Fibonacci again and again

题目大意不想复制题目了,太类似了。还是Nim游戏,这次只能取斐波那契数列里的数,数值<=1000
SG(x)=mex{ SG( x-Fibo[i] ) }
这份代码省去了第一题的a和mex函数,效率上应该高一点。

///I'm not weak at all
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAX 1000+10
using namespace std;
bool flag[MAX];
int sg[MAX];
int fibo[20];
void SG(int x)
{
	memset(flag,0,sizeof(flag));
	for(int i=1;i<=17;i++)
	{
		if(x<fibo[i])
			break;
		flag[sg[x-fibo[i]]]=1;
	}
	for(int i=0;i<MAX;i++)
		if(!flag[i])
		{
			sg[x]=i;
			return;
		}
	cout<<"ERROR";
}



void Fibo()
{
	fibo[1]=1;
	fibo[2]=1;
	for(int i=3;i<=17;i++)
		fibo[i]=fibo[i-1]+fibo[i-2];
	return ;
}


int main()
{
	int m,n,p;
	int ans;
	while(1)
	{
		scanf("%d%d%d",&m,&n,&p);
		if(!m&&!n&&!p)
			break;
		Fibo();
		memset(sg,0,sizeof(sg));
		for(int i=0;i<MAX;i++)
			SG(i);
		ans=sg[m]^sg[n]^sg[p];
		if(ans)
			printf("Fibo\n");
		else printf("Nacci\n");
	}

	return 0;
	
}

Nim or not Nim?

Problem Description
Nim is a two-player mathematic game of strategy in which players take turns removing objects from distinct heaps. On each turn, a player must remove at least one object, and may remove any number of objects provided they all come from the same heap.

Nim is usually played as a misere game, in which the player to take the last object loses. Nim can also be played as a normal play game, which means that the person who makes the last move (i.e., who takes the last object) wins. This is called normal play because most games follow this convention, even though Nim usually does not.

Alice and Bob is tired of playing Nim under the standard rule, so they make a difference by also allowing the player to separate one of the heaps into two smaller ones. That is, each turn the player may either remove any number of objects from a heap or separate a heap into two smaller ones, and the one who takes the last object wins.
Input
Input contains multiple test cases. The first line is an integer 1 ≤ T ≤ 100, the number of test cases. Each case begins with an integer N, indicating the number of the heaps, the next line contains N integers s[0], s[1], …, s[N-1], representing heaps with s[0], s[1], …, s[N-1] objects respectively.(1 ≤ N ≤ 10^6, 1 ≤ S[i] ≤ 2^31 - 1)
Output
For each test case, output a line which contains either “Alice” or “Bob”, which is the winner of this game. Alice will play first. You may asume they never make mistakes.
Sample Input
2
3
2 2 3
2
3 3
Sample Output
Alice
Bob
题目大意这就是那个坑了我两三个小时的题。这个题是在Nim游戏规则上再加上,这个人本回合不仅可以移走石子,还可以把一堆石子分成两堆。
哎呦喂,这个题的小石头会生小崽子,这可怎么弄啊?
怪我上课没好好听老师讲课,这个题求SG的思想一样,只是稍稍有点点不同。
比如石子数目为3的一堆
它的后继元素为0,1,2,(1、2)四种,其中SG((1、2))不就是SG(1)^SG(2)么,求这四个元素的mex函数得到SG(3).
之后打表得到规律,规律好写,看代码吧,重要的是思想要搞明白

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int SG(int x)
{
	if(x%4==3)
		return x+1;
	if(x%4==0)
		return x-1;
	else return x;
}
int main()
{
	int T,n;
	int nim;
	int a;
	cin>>T;
	while(T--)
	{
		scanf("%d",&n);
		scanf("%d",&a);
		nim=SG(a);
		for(int i=1;i<n;i++)
		{
			scanf("%d",&a);
			nim=nim^SG(a);
		}
		if(nim==0)
			printf("Bob\n");
		
		else
			printf("Alice\n");
		
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值