HDU1847 —— Good Luck in CET-4 Everybody!
题意
:n张牌,轮流抓牌,每次抓的牌数是2的幂次,最后抓完牌的胜。
思路
:首先写在这个汇总题集的最前面,博弈问题,大致分为三类,① 经典模型 ② SG函数 ③ 找规律,而此题集主要针对的也是此类找规律问题。
对于此题,的确可以用 函数解决,但是我们可以先找一下规律,不要着急下手。(实测,SG函数会T…)
此处用W表示Win,F表示Fail。1 - W 、2 - W、3 - F、4 - W、5 - W、6 - F…列的再多一些,就可以明显地发现当n为3倍数时,Fail,否则Win。这也是找规律的常见手段,枚举状态找规律。
代码
:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 1000+100;
const int M = 1e5+100;
const db EPS = 1e-9;
using namespace std;
int n;
int main()
{
while(~scanf("%d",&n))
{
if(n%3 == 0) printf("Cici\n");
else printf("Kiki\n");
}
return 0;
}
/*
n张牌,轮流抓牌,每次抓的牌数是2的幂次,最后抓完牌的胜
*/
HDU2147 —— kiki’s game
题意
:推方格,从(n,m)开始推,每次只能往左一格,往下一格,往左下一格,不能推了则为输。
思路
:博弈问题本质是个游戏,拿到问题的第一步应该是对于小状态进行模拟,很多简单找规律的问题都会迎刃而解。
表示必胜态, 表示必败态,可以发现 出现的位置是固定的,即行数列数均为奇数的地方,由此此题解决。
代码
:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 2000+1;
const int M = 1e5+100;
const db EPS = 1e-9;
using namespace std;
int n,m;
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n == 0 || m == 0) break;
if(n%2 && m%2) printf("What a pity!\n");
else printf("Wonderful!\n");
}
return 0;
}
/*
推方格,从(n,m)开始推,每次只能往左一格,往下一格,往左下一格,不能推了则为输。
*/
POJ1740 —— A New Stone Game
题意
:对于n堆石子,每堆若干个,两人轮流操作,每次操作分两步。① 从某堆中去掉至少一个 ② (可省略) 把该堆剩余石子的一部分分给其它的某些堆。最后谁无子可取即输。
思路
:此时可以先考虑只有两堆石子的时候,应该如何判断。不难发现,假如两堆石头一样多,则后者可以完全模仿前者的操作 (“模仿”与“两两分组”是最常见的博弈手段之一),因此我们可以考虑将相同堆数的石头进行两两分组。
不难发现,如果石头堆数为偶数个,且恰好可以两两分组,并且每组中石头个数一致,则一定是必败态。即下图这种情况。
然后我们来证明其他所有情况均可转化为必胜态。假如 为奇数,则一定可以利用最多的那堆石子,将其他 堆石子进行两两分组。如下图所示,利用A部分使得第一堆与第二堆相同,利用B部分使第三堆和第四堆相同。
如果石子个数为偶数,但是并不能恰好两两分组,则将最大的一堆石子与最小的那堆石子进行匹配,然后利用最大的那堆石子填充其余每组石子之间的差距,构造出一个必败态。
代码
:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 1e5+100;
const int M = 1e5+100;
const db EPS = 1e-9;
using namespace std;
int n,vis[N];
int main()
{
while(~scanf("%d",&n)){
if(!n) break;
rep(i,0,100) vis[i] = 0;
rep(i,1,n){
int xx; scanf("%d",&xx);
vis[xx]++;
}
if(n%2 == 1){
printf("1\n");
continue;
}
int jud = 0;
rep(i,0,100){
if(vis[i]%2 == 1) jud = 1;
}
if(jud) printf("1\n");
else printf("0\n");
}
return 0;
}
/*
对于n堆石子,每堆若干个,两人轮流操作,每次操作分两步.
第一步从某堆中去掉至少一个.
第二步(可省略)把该堆剩余石子的一部分分给其它的某些堆。最后谁无子可取即输。
*/
HDU4388 Stone Game II
题意
:
堆石子,每堆石子有一定的数目,每次操作任选一堆石子,该堆石子原数目为
,取石子至仅剩
个,要求k ^ x < x。取完石子后再增加一堆石子,石子个数为k ^ x。每个选手在每轮游戏中仅有一次机会将增加的那一堆石子个数改为2*k^x。问谁能获胜。
思路
:这个题的主要难点在于如何处理k ^ x < x的问题,这个限制有什么性质。因此我们需要打表,查看k与k ^ x与x的关系,打表之后可以发现无论如何取,x与k ^ x二进制形式中
的个数与
中
的个数奇偶性相同。
假设一堆个数为 的石子,二进制中有 个 ,最后分为了 堆,由于最后每一堆石子个数二进制形式中都只有 个 ,因此 与 奇偶性相同。而 堆即分了 次,所以通过 的奇偶性即可判定先手胜负。
代码
:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 1e5+100;
const int M = 1e5+100;
const db EPS = 1e-9;
using namespace std;
int countt(int x){
int num = 0;
while(x){
if(x&1) num++;
x >>= 1;
}
return num;
}
int main()
{
int _,n,tp; scanf("%d",&_);
rep(kk,1,_){
scanf("%d",&n); tp = 0;
rep(i,1,n){
int xx; scanf("%d",&xx);
tp += countt(xx)-1;
}
printf("Case %d: ",kk);
if(tp%2) printf("Yes\n");
else printf("No\n");
}
}
/* 打表程序
int main()
{
int n = 100;
rep(i,1,n){
printf("************\n");
LOG2("i",i,"num",countt(i));
rep(j,1,i-1){
if((j^i) < i){
LOG3("j",j,"j^i",(j^i),"num",(countt(j)+countt(j^i)));
}
}
}
return 0;
}*/
总结:
找规律问题经常在博弈问题中出现,不属于特定的模型,但是需要找到规律。而找规律通常的方法有 ① 小状态枚举 ② 打表找规律 ③ 奇偶讨论、两两分组、模仿先手行为等策略。