博弈专辑
博弈知识汇总:http://www.cnblogs.com/crazyac/articles/1954607.html
Nim博弈Nim博弈,如果出现不是1的情况,则所有情况做异或操作。如果所有的数量都是1则看n的奇偶性。
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
// freopen("c:/aaa.txt", "r", stdin);
int T, a, res, n, i;
bool flag;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
flag = true;
res = 0;
for(i=0; i<n; ++i) {
scanf("%d", &a);
if(a != 1) flag = false;
res ^= a;
}
res = res ? 0 : 1;
((flag) ? (n&1) : res) ? puts("Brother") : puts("John");
}
return 0;
}
Nim博弈和hdoj 1907差不多
#include <iostream>
using namespace std;
int main() {
// freopen("c:/aaa.txt", "r", stdin);
int T, a, res, n, i;
bool flag;
while(scanf("%d", &n) == 1 && n) {
flag = true;
res = 0;
for(i=0; i<n; ++i) {
scanf("%d", &a);
res ^= a;
}
res ? puts("Rabbit Win!") : puts("Grass Win!");
}
return 0;
}
转:首先我们可以想到在面对3的时候是必败局,谁面对3时无论拿多少都会败 ! <---这是关键
那么就要尽量造成这样的局势给对方,因为任何不是3的倍数的数加1或2都可以变成3的倍数,
同理减去1或2也可以变成3的倍数,也就是说假设目前的个数不是3的倍数,那我肯定能把它
拿成3的倍数,比如现在是11个,那我拿走2个就变成9,这样就造成对方为3的倍数局势,那
么对方拿m个我都可以通过拿1或者2使总共一轮拿的数目成为3的倍数,这样就会有两种情况:
1.刚好拿完.
2.剩下的还有3的倍数个,那继续;
所以这样拿下去必胜
#include <iostream>
using namespace std;
int main() {
int n;
while(scanf("%d", &n)!=EOF) {
(n%3 != 0) ? puts("Kiki") : puts("Cici");
}
return 0;
}
4.hdoj 1848 Fibonacci again and again
SG函数,是sg[x]就是x的后继点的SG值中没有出现过的最小值。x的后继点,即从x取走一个fibonacci数后的点。
所以把点x的所有后继点的sg值标记起来,然后从小到大找就可以得到x的sg值了。
#include <iostream>
using namespace std;
const int maxn = 1001;
int mark[maxn], sg[maxn], f[15];
int m, n, p;
void creat() {
int i, j;
memset(sg, 0, sizeof(sg));
f[0] = 1; f[1] = 2;
for(i=2; i<15; ++i) f[i] = f[i-1] + f[i-2];
for(i=1; i<=1000; ++i) {
memset(mark, 0, sizeof(mark));
for(j=0; j<15; ++j) {
if(i - f[j] >=0) mark[sg[i-f[j]]] = 1;
else break;
}
for(j=0; j<maxn; ++j) {
if(mark[j] == 0) {
sg[i] = j;
break;
}
}
}
}
int main() {
// freopen("c:/aaa.txt", "r", stdin);
creat();
while(scanf("%d %d %d", &n, &m, &p) == 3) {
if(!n && !m && !p) break;
(sg[n] ^ sg[m] ^ sg[p]) ? puts("Fibo") : puts("Nacci") ;
}
return 0;
}
5.hdoj 1850 Being a Good Boy in Spring Festival
Nim 博弈,思路见下:
MiYu原创, 转帖请注明 : 转载自 ______________白白の屋
一.如果a1^a2^a3^...^an=0 ( 即 : nim-sum=0 ) , 说明先手没有必赢策略, 方法数肯定为 0;
二.假设先手的人有必赢策略。
问题则转化为=>在任意一堆拿任意K张牌,并且剩下所有堆的nim-sum=0(P-position)的方案总数。
1. 现在我们先看一个例子(5,7,9),并假设从第一堆取任意K张牌。
排除第一堆牌的nim-sum为 7^9=14
0111
^1001
-------
1110
如果要使所有堆的nim-sum=0成立,则第一堆取掉K张以后必定为1110,因为X^X=0。
所以要观察 5-k=14 k>0 成立,此例子(在第一堆取任意K张牌)明显的不成立。但并不代表在第二或第三堆取任意K张牌的解不成立。
2. 现在看第二个例子(15,7,9),并假设从第一堆取任意K张牌。
排队第一堆牌的nim-sum为7^9=14,和第一个例子相同,所以问题变为观察 15-k=14 k>0 是否成立。
当然这个例子是成立的。
三.总结得出:
在任意一堆拿任意K张牌,并且所有堆的nim-sum=0 成立的条件为:排除取掉K张牌的那一堆的nim-sum必须少于该堆牌上的数量(例子二),否则不能在此堆上取任意K张牌使所有堆的nim-sum=0成立(例子一)。
故总方案数为 ( 在任意一堆拿任意K张牌,并且所有堆的nim-sum=0 成立 ) 的总数。
#include <iostream>
using namespace std;
int main() {
// freopen("c:/aaa.txt", "r", stdin);
int n, i, num[101], res, cnt;
while(scanf("%d", &n) == 1 && n) {
res = 0;
for(i=0; i<n; ++i) {
scanf("%d", &num[i]);
res ^= num[i];
}
if(res == 0) {
printf("0\n");
continue;
}
cnt = 0;
for(i=0; i<n; ++i) {
if((res ^ num[i]) < num[i]) ++ cnt;
}
printf("%d\n", cnt);
}
return 0
}
SG
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int num[101], A[101], SG[10001], k;
//求SG模版
int SG_value(int n) //求SG值
{
int i,temp;
int judge[100]={0}; //用于寻找最小的SG值做标记
for(i=0;i<k;i++)
{
temp = n - num[i];
if(temp<0)
break;
if(SG[temp]==-1) //寻找最小的SG数
SG[temp]=SG_value(temp);
judge[SG[temp]]=1; //为后继点加上标记
}
for(i=0;;i++) //找到每一个SG[i]的最小SG值
{
if(judge[i]==0)
return i;
}
}
int main() {
// freopen("c:/aaa.txt", "r", stdin);
int i, a, res, n, m;
while(scanf("%d", &k) == 1 && k) {
for(i=0; i<k; ++i) scanf("%d", &num[i]);
sort(num, num+k);
memset(SG, -1, sizeof(SG));
scanf("%d", &m);
while(m--) {
scanf("%d", &n);
res = maxn = 0;
for(i=0; i<n; ++i) {
scanf("%d", &a);
res ^= SG_value(a);
}
res ? putchar('W') : putchar('L') ;
}
printf("\n");
}
return 0;
}
7.hdoj 1517 A Multiplication Game
#include <iostream>
using namespace std;
typedef __int64 lld;
int main() {
lld x, y, n;
while(scanf("%I64d", &n) == 1) {
x = 1;
y = 9;
if(x < n && n <= y) {
puts("Stan wins.");
continue;
}
while(true) {
x *= 9;
y *= 2;
if(x < n && n <= y) {
puts("Ollie wins.");
break;
}
x *= 2;
y *= 9;
if(x < n && n <= y) {
puts("Stan wins.");
break;
}
}
}
return 0;
}
寻找必败态。设必败态为x,箱子容量为s,则x+x*x < s , (x+1) + (x+1)*(x+1) >=s ;然后找到大于c的最小必败态。
#include <iostream>
#include <cmath>
using namespace std;
int getSG( int s, int c ) {
int x = (int)sqrt((double)s);
while( x * x + x >= s ) --x;
if( c > x ) return s - c;
else return getSG( x, c );
}
int main() {
// freopen( "c:/aaa.txt", "r", stdin );
int n, a, b, res, ca=1;
while( scanf("%d", &n) == 1 && n ) {
printf( "Case %d:\n", ca++ );
res = 0;
while( n-- ) {
scanf( "%d %d", &a, &b );
res ^= getSG( a, b );
}
res ? puts( "Yes" ) : puts( "No" );
}
return 0;
}
威佐夫博弈
#include <iostream>
#include <cmath>
using namespace std;
int main() {
// freopen( "c:/aaa.txt", "r", stdin );
int a, b, c, k;
while( scanf( "%d %d", &a, &b ) == 2 ) {
if( a > b ) swap( a, b );
k = a * 1.0 * (sqrt(5.0) - 1) / 2.0;
c = k * (sqrt(5.0) + 1) /2.0 ;
if( c == a ) {
if( a + k == b ) puts( "0" );
else puts( "1" );
} else {
k ++;
c = k * (sqrt(5.0) + 1) /2.0 ;
if( c == a && b == a + k ) puts( "0" );
else puts( "1" );
}
}
return 0;
}
两个人从一堆石子里取出1-m个石子,判断胜者。
#include <iostream>
using namespace std;
int main() {
int T, n, m;
scanf("%d", &T);
while(T--) {
scanf("%d %d", &n, &m);
(n%(m+1) != 0) ? puts("first") : puts("second");
}
return 0;
}
浙公网安备 33010602011771号