看了看《挑战程序设计竞赛》的4.2博弈方便的知识点。
感觉也真是聪明啊……
整理整理,锻炼大脑~
1】Alice和Bob在玩这样一个游戏:给定k个数字a1,a2…ak。一开始,有x个石子,Alice和Bob轮流取石子。每次所取石子的个数一定要在a1~ak中。Alice先取。取走最后一个石子的一方获胜。当双方都采取最优策略时,谁会获胜?题目假定a1~ak中一定有1.
分析题目,有三种情况:
1.没有剩余,也就是只剩下了0个,那么必败
2.对于某个i(1<=i<=k),j-A[i]必败那么j就是必胜(可以从剩下的j中拿去A[i],达到对方的必败状态)
3.对于某个i(1<=i<=k),j-A[i]必胜那么j就是必败(对方可以从剩下的j中拿去A[i])
建立一个win[j]数组,表示轮到玩家拿的时候剩余j时的胜负状态。
#include <cstdio>
#define maxn 105
int X,K,A[maxn];
bool win[maxn];
void slove(){
win[0]=false;
for(int j=1;j<=X;j++){
//如果对手达到必败,那么必胜
win[j]=false;
/*三种都可
for(int i=0;i<K;i++){ //如果可取的A[i]<=剩余的j,进入判断
win[j] |= ( A[i]<=j && !win[j-A[i]]);
}
for(int i=0;i<K&&A[i]<=j;i++){
win[j] |= !win[j-A[i]] ;
}
*/
for(int i=0;i<K&&A[i]<=j;i++){
if(!win[j-A[i]])
win[j] |=true ;
}
}
if(win[X]) puts("Alice");
else puts("Bob");
}
int main() {
scanf("%d%d",&X,&K);
for(int i=0;i<K;i++){
scanf("%d",&A[i]);
}
slove();
return 0;
}
2】POJ2484
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 4824 | Accepted: 2997 |
Description
Figure 1
Note: For n > 3, we use c1, c2, ..., cn to denote the coins clockwise and if Alice remove c2, then c1 and c3 are NOT adjacent! (Because there is an empty place between c1 and c3.)
Suppose that both Alice and Bob do their best in the game.
You are to write a program to determine who will finally win the game.
Input
Output
Sample Input
1 2 3 0
Sample Output
Alice Alice Bob
n枚硬币排成一个圈,
Alice和Bob轮流从中取1~2枚硬币,取得2枚硬币要相连,取走最后一枚硬币获胜。双方采取最优策略,谁会获胜?
如果两个人取硬币的的状态一样(中间不连续),则是一种必败状态。
A取硬币,B取可以达到 和A同样状态的 硬币,那么B必胜。
所以,Alice在第一步取了1枚或者2枚硬币之后,原本成圈的硬币编程了长度为n-1或者n-2的链,
只要B在中间位置根据奇偶性取走硬币就可以把硬币扽成长度相同的链。
然后A就必败了…
当然除了N<=2的情况。
好吧,思维绕一点,但是想清楚了,代码一下就出来…所以这种题考的实际就是智商…多做多看~…
#incldue <cstdio>
int main(){
int n;
scanf("%d",&n);
if(n<+2) puts("Alice");
else puts("Bob");
return 0;
}
3.Poj2348
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 8594 | Accepted: 3500 |
Description
25 7 11 7 4 7 4 3 1 3 1 0
an Stan wins.
Input
Output
Sample Input
34 12 15 24 0 0
Sample Output
Stan wins Ollie wins
辗转相除博弈游戏
预处理:
1.先调整使得a<b
2.若b%a==0 必胜
判断:
1】.b-a<a:只有一种选择,就是减去一个a。
若减去之后为必胜,则自己必败。
若减去之后为必败,则自己必胜。
2】.b-a>a:多种选择,
假设x是使得b-x*a<a的整数,如果从b-(x-1)*a之后,也就达到了状态1 。
2.1如果减去之后是必败态:
则当前状态是必胜态
----------
2.2如果减去之后是必胜态,此时可以减去a*x,这样就达到了2.1所说的必胜态的下一个状态,也就是必败态
所以当前状态是必胜态
---------
Eg:
(4,17)如果17-12=5也就是(4, 5)必败,所以直接-12就能必胜
(4,19)如果19-12=7也就是(4,7),对方必胜,那么直接减去16达到(4,7)对的下一个(4,3)必败状态给对手
也就扭转了局面。达到必胜态
所以,从初始状态开始,首先达到自由度的2状态的一方获胜。
注意这里循环中每次的first状态发生变化
因为每种状态都是互推的,这次的必胜,下次的必败。
所以当遇到可以停止的时候break;
#include <cstdio>
void swap(int &x,int &y){
int t;
t=x,x=y,y=t;
}
int main(){
int a,b;
while(scanf("%d%d",&a,&b)!=EOF){
if(a==0&&b==0) break;
bool first=true;
while(true){
if(a>b) swap(a,b);
//第一种情况,b是a的倍数。必胜。
if( b%a ==0 ) break;
//第二种情况,必胜。
if( b-a > a ) break;
b-=a;
first=!first;
}
if(first) puts("Stan wins");
else puts("Ollie wins");
}
return 0;
}