博弈论

来源:互联网 发布:威锋认证的淘宝店 编辑:程序博客网 时间:2024/04/18 23:37

1、水题,裸的纳什博弈。

HDU 1846

用推导做出的,比如一次最多取k个石子,总共有n个石子,那么剩下石子数为1-k时,取的人必赢,但剩下k+1的时候,根据最优策略的原则,

那么此时取的人必输,由此可以推导剩下k+1个石子的时候,这个人可以使得石子变成1-k个,由于全部是必胜点,所以这个人无论怎么取都是输,

当取1-k个石子时,这些石子存在必败点时,这个点就是必胜的,如果取后无法达到必败点,这个点就是必败点,由此推可得:

#include<iostream>#include <stdio.h>#include <string.h>#include<stdlib.h>#include <map>#include <set>#include <queue>#include <math.h>#include <algorithm>#include <vector>using namespace std;int q1[5000];int main(){    //freopen("in.txt","r",stdin);    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;    cin >> m;    while(m--){    cin >> n >> k;    memset(q1,0,sizeof(q1));    for(i=1;i<=k;i++)        q1[i]=1; //1-k全部是必胜    for(i=k+1;i<=n;i++){        t1=0;        for(j=1;j<=k;j++)            if(q1[i-j]==0)t1=1;        if(t1==1)            q1[i]=1;        else            q1[i]=0;    }    if(q1[n]==1)    cout << "first" <<endl;    else    cout << "second" <<endl;    }    return 0;}
第二种做法就是,先手的人一直使后手的人处于必败点,也就是比如k为3,n为15,就把15分成4,4,4,3,这样每次根据对手拿的数量,使得拿的数量正好为一堆,让对手一直处于必败点,那么就可以赢了。

也就是n%(k+1)==0为先手必输,其他都为先手必赢。

#include<iostream>#include <stdio.h>#include <string.h>#include<stdlib.h>#include <map>#include <set>#include <queue>#include <math.h>#include <algorithm>#include <vector>using namespace std;int q1[5000];int main(){    //freopen("in.txt","r",stdin);    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;    int T;    cin >> T;    while(T--){    cin >> n >> k;    if(n%(k+1)==0){    //n%k+1和 n%(k+1)是不一样的        cout << "second" <<endl;    }else{        cout << "first" <<endl;    }    }    return 0;}

威佐夫博弈:

有两堆物品,一个人可以取一堆任意多/2堆中取同样多的数量,每次至少取1个,最先取完者获胜。

当一个人面对一个局势如(0,0)(1,2)  (3,5)称之为奇异局势,也就是必败的局势bk=ak+k;ak是指在之前没有出现过的最小自然数。

(4,7)(6,10)等等都是奇异局势。

任何操作都可以将奇异局势变成非奇异局势,如果2个值同时减少,其差值必定不可能是其他奇异局势的差。

如果只减少1个,那么这个奇异局势的差值改变了,也就不是奇异局势了。

 那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
    ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,...,n 方括号表示取整函数)
奇妙的是其中出现了黄金分割数(1+√5)/2 = 1。618...,因此,由ak,bk组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。

判断是否为奇异模式:”

while(cin >> a >> b){j=a*(sqrt(5)-1)/2;a1=j*(sqrt(5)+1)/2;a2=(j+1)*(sqrt(5)+1)/2;if(a==a1)cout << "b= " << a+j<<endl;else if(a==a2)cout << "b= " << a+j+1 << endl;elsecout << "不是奇异模式的a" << endl; if(a==a1&&a+j==b||((a+j+1==b)&&(a==a2)))cout << "是" <<endl;elsecout <<"不是"<< endl;}
hdu 2177

输入为2个石子,0 0 结束

输出也有若干行,如果最后你是败者,则为0,反之,输出1,并输出使你胜的你第1次取石子后剩下的两堆石子的数量x,y,x<=y。如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况.

#include<iostream>#include<math.h>using namespace std;int a[1000000][2];int temp=1000000;int temp3=0;void find(){     int temp2=0;        while(temp2<=temp){        a[temp3][0]=int(temp3*(1+sqrt(5))/2);        a[temp3][1]=a[temp3][0]+temp3;         temp2=a[temp3][1];         temp3++;         }  //   cout<<temp3<<endl;         }int main(){    find();    int n1,n2;    while(cin>>n1>>n2){           if(n1==0&&n2==0)                 break;           double tempor=(sqrt(5)+1)/2;           if(n1==(int)((n2-n1)*tempor)){                     cout<<"0"<<endl;                     continue;                     }           cout<<"1"<<endl;           int i=0;           int sub=n2-n1;           if(n1==n2){               cout<<"0 0"<<endl;               }                                                        else if(((n1-a[sub][0])==(n2-a[sub][1]))&&((n1-a[sub][0])>0)&&((n2-a[sub][1])>0))                  cout<<a[sub][0]<<" "<<a[sub][1]<<endl;          for(int j=0;j<temp3;j++){                  if(a[j][0]==n1){                         cout<<a[j][0]<<" "<<a[j][1]<<endl;                         }                  if(a[j][1]==n1){                         cout<<a[j][0]<<" "<<a[j][1]<<endl;                         break;                         }                         }                         }           return 0;           } 

HDU 2516

取石子游戏

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5157 Accepted Submission(s): 3116


Problem Description
1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".

Input
输入有多组.每组第1行是2<=n<2^31. n=0退出.

Output
先取者负输出"Second win". 先取者胜输出"First win".
参看Sample Output.

Sample Input
213100000

Sample Output
Second winSecond winFirst win
斐波那契博弈,如果是斐波那契数,就后手赢。

SG函数:

Good Luck in CET-4 Everybody!

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 9685 Accepted Submission(s): 6275


Problem Description
大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此。当然,作为在考场浸润了十几载的当代大学生,Kiki和Cici更懂得考前的放松,所谓“张弛有道”就是这个意思。这不,Kiki和Cici在每天晚上休息之前都要玩一会儿扑克牌以放松神经。
“升级”?“双扣”?“红五”?还是“斗地主”?
当然都不是!那多俗啊~
作为计算机学院的学生,Kiki和Cici打牌的时候可没忘记专业,她们打牌的规则是这样的:
1、 总共n张牌;
2、 双方轮流抓牌;
3、 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?
当然,打牌无论谁赢都问题不大,重要的是马上到来的CET-4能有好的状态。

Good luck in CET-4 everybody!

Input
输入数据包含多个测试用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。

Output
如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。

Sample Input
13

Sample Output
KikiCici


#include <iostream>#include <string.h>#include <math.h>#include <stdio.h>#include <vector>#include <algorithm>#include <map> using namespace std;int sg[1050];int arr[20];int mex(int x){if(sg[x]!=-1)return sg[x];bool vis[1050];int i;memset(vis,0,sizeof(vis));for(i=0;i<10;i++){int temp=x-arr[i];if(temp<0)break;int res=mex(temp);vis[res]=1;}for(i=0;i<1050;i++)if(!vis[i]){sg[x]=i;//cout << x <<  "--- " << i << endl;break;}return sg[x];}int main(){//freopen("in.txt","r",stdin);int i,j,k,f1,f2,f3,f4,t4,t1,t2,t3,n,m;int T,r,c;memset(sg,-1,sizeof(sg));arr[0]=1;sg[0]=0;for(i=1;i<=10;i++)arr[i]=arr[i-1]*2;while(cin >> n){if(!mex(n))cout << "Cici" << endl;else cout << "Kiki" << endl; }return 0;}


1 0
原创粉丝点击