博弈专辑

博弈知识汇总:http://www.cnblogs.com/crazyac/articles/1954607.html

1.hdoj 1907 John

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;
}

2.hdoj 1849 Rabbit and Grass

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.hdoj 1847 Good Luck in CET-4 Everybody!

转:首先我们可以想到在面对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
}

6.hdoj 1536 S-Nim

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;
}

8.hdoj 1729 Stone Game

寻找必败态。设必败态为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;
}
   

9.hdoj 1527 取石子问题

威佐夫博弈

#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;
}

10.hdoj 1846 Brave Game

 两个人从一堆石子里取出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;
}
posted on 2011-02-13 16:04  CrazyAC  阅读(286)  评论(0)    收藏  举报