博弈论习题

来源:互联网 发布:无极郭敬明知乎 编辑:程序博客网 时间:2024/05/19 04:27

poj2234

真。裸题。

#include<cstdio>#include<cstring>#include<algorithm>using  namespace std;int main(){    int n;    while(~scanf("%d",&n))    {        int ans=0,s;        for(int i=0;i<n;i++)        {            scanf("%d",&s);            ans^=s;        }        if(ans==0)            printf("No\n");        else printf("Yes\n");    }}

Hdu2176
一看知道是尼姆博弈,暴力做,TLE了,在这里记一下,有个好神奇的东西,一个数n和m异或之后再和m异或会重新变成n。还是太菜了,看了尼姆博弈,但是过程也看的不是特别细,第一次取石子还是没理解透彻。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int a[200005];int main(){    int n;    while(~scanf("%d",&n)&&n)    {        int t=0;        for(int i=0;i<n;i++)        {            scanf("%d",&a[i]);            t=t^a[i];        }        if(t==0)            printf("No\n");        else {            int temp;            printf("Yes\n");            for(int i=0;i<n;i++)            {                temp=t^a[i];                if((temp^t^a[i])==0)                {                    if((t^a[i])<a[i])                    printf("%d %d\n",a[i],t^a[i]);                }            }        }    }}

poj2975
和上面的题差不多,也是求第一步能有几种走法,把上面那个改一改就差不多了。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int a[1005];int main(){    int t;    while(~scanf("%d",&t)&&t)    {        int ans=0;        for(int i=0; i<t; i++)        {            scanf("%d",&a[i]);            ans^=a[i];        }        int sum=0;        if(ans==0)        {            printf("0\n");            continue;        }        else        {            int temp;            for(int i=0; i<t; i++)            {                temp=ans^a[i];                if((temp^ans^a[i])==0&&(ans^a[i])<a[i])                {                    sum++;                }            }        }        printf("%d\n",sum);    }}

Hdu1730
没什么好说的,裸地尼姆博弈

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;int a[1005];int main(){    int n,m;    while(~scanf("%d %d",&n,&m))    {        int aa,b,t=0;        for(int i=0;i<n;i++)        {            scanf("%d %d",&aa,&b);            a[i]=max(aa,b)-min(aa,b)-1;            t=t^a[i];        }        if(t==0)            printf("BAD LUCK!\n");        else printf("I WIN!\n");    }}

poj2960
比较经典的SG函数的题吧,一直WA。。。原因是求SG值的地方错了(虽然我也不知道哪里错了)蓝瘦。。。。。
题意比较明显,就是告诉你一个集合S,只能拿这个集合里有的数,有n个例子,每个例子有m堆,把这m堆分解成m个子游戏,分别求SG值,然后再异或,得到答案是0就输出L不然就输出W(注意他是n个例子每个例子下面就是输出不要攒到一起输出,我就被样例给骗了。)还是自己太弱了吧。继续加油。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int s[105],d,dp[10005],t[105];bool vis[10005];const int temp =10005;int main(){    int n,m,k;    while(~scanf("%d",&n)&&n)    {        memset(dp,0,sizeof dp);        memset(vis,0,sizeof vis);        memset(s,0,sizeof s);        memset(t,0,sizeof t);        for(int i=0; i<n; i++)            scanf("%d",&s[i]);        sort(s,s+n);        dp[0]=0;        for(int i=1; i<temp; i++)        {            memset(vis,0,sizeof vis);            for(int j=0; j<n; j++)            {                if(i-s[j]>=0)                    vis[dp[i-s[j]]]=1;                else break;            }            for(int j=0;; j++)            {                if(!vis[j])                {                    dp[i]=j;                    break;                }            }        }        scanf("%d",&m);        int mount=0;        for(int i=0; i<m; i++)        {            scanf("%d",&k);            int ans=0;            for(int j=0; j<k; j++)            {                scanf("%d",&d);                ans=ans^(dp[d]);            }            if(ans==0)            {                printf("L");            }            else printf("W");        }        printf("\n");    }}

网上都是非常简短的函数,我写的有些丑。

poj1067
威佐夫博弈的裸题。给你x,y,如果x>y,交换。z=y-x。
然后w=(int)(((sqrt(5.0)+1)/2)*z)
然后判断w和x相不相等。相等就是先手必败,否则就是必胜。

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;int main(){    int n,m;    while(~scanf("%d %d",&n,&m))    {        int x=min(n,m);        int y=max(n,m);        int z=y-x;        int w=(int)(((sqrt(5.0)+1)/2)*z);        if(w==x)            printf("0\n");        else printf("1\n");    }}

poj2425
开始读题一直没读懂。搞样例搞了好久。。。。
大概就是给你一个图,然后告诉你上面那几个点上有棋子,然后双方轮流走谁先无棋可走谁就输了。和SG函数的定义很像。因为一些细节一直做不出来。。。。建图建的不好,想省事顺便练练存图,用vector结果初始化-1卡住了,又改回二维数组。费了老大功夫。。以后看数据量不大还是用二维数组吧。。。。

#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;int adj[1050][1050];int sg[1005];int n;int serch(int n1){    bool vis[1005];    if(sg[n1]!=-1)        return sg[n1];    memset(vis,0,sizeof vis);    for(int i=0; i<n; i++)    {        if(adj[n1][i]!=-1)        {            vis[serch(i)]=1;        }    }    int w=0;    while(vis[w])w++;    return sg[n1]=w;}int main(){    while(~scanf("%d",&n))    {        memset(sg,-1,sizeof sg);        memset(adj,-1,sizeof adj);        for(int i=0; i<n; i++)        {            int m,t;            scanf("%d",&m);            if(m==0)            {                sg[i]=0;            }            for(int j=0; j<m; j++)            {                scanf("%d",&t);                adj[i][t]=1;            }        }        int m;        while(~scanf("%d",&m)&&m)        {            int x;            int ans=0;            for(int i=0; i<m; i++)            {                scanf("%d",&x);                ans^=serch(x);            }            if(ans==0)                printf("LOSE\n");            else printf("WIN\n");        }    }}

poj1704
做过一次的,还错了。。。
题意是一行棋盘上有一堆棋子,然后轮流向左移。谁先无法移动谁输。
将两个棋子之间的格子数目看做一堆棋子,如果总棋子数是奇数,就把第一个和边界之间的格子数目看成一堆棋子(注意,奇数)然后nim博弈。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int main(){    int p[1000];    int t,n;    scanf("%d",&t);    while(t--)    {        memset(p,0,sizeof p);        scanf("%d",&n);        int mount=0;        if(n%2==1)            p[mount++]=0;        for(int i=0; i<n; i++)        {            int a;            scanf("%d",&a);            p[mount++]=a;        }        sort(p,p+mount);        int x=0;        for(int i=0; i+1<mount; i+=2)        {            x^=(p[i+1]-p[i]-1);        }        if(x==0)            printf("Bob will win\n");        else puts("Georgia will win");    }}

poj1740
没做出来,看的题解。。。
大概就是偶数的时候如果排序之后的12、34…..都相等的话,无论先手怎么走后手模仿先手必败。其他就是先手必胜
而奇数先手只要把最大的拿走然后创造一个12 34 ….都相等的局面就好了。先手必胜。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int a[1005];int main(){    int n;    while(~scanf("%d",&n)&&n)    {        memset(a,0,sizeof a);        for(int i=0; i<n; i++)            scanf("%d",&a[i]);            int flag=0;        if(n%2==1)        {            printf("1\n");            continue;        }        else {                sort(a,a+n);            for(int i=0;i<n;i+=2)            {                if(a[i]!=a[i+1])                {                    flag=1;                }            }        }        printf("%d\n",flag);    }}

poj2068
看着网上的一堆题解发憷,都是当水题来做,蓝瘦。
dp[i][j]表示第i个人时候还有j个石子,然后当j=0时为1;
后继里面有存在0的这个状态就是1;

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int a[30],dp[30][8200];int n,s;int slove(int x,int S){    if(dp[x][S]!=-1)return dp[x][S];    for(int i=1; i<=a[x]; i++)    {        int t=S-i;        if(t<0)            break;        int y;        if(x+1>=2*n)y=0;        else y=x+1;        if(slove(y,t)==0)return dp[x][S]=1;    }    return dp[x][S]=0;}int main(){    while(~scanf("%d",&n)&&n)    {        memset(a,0,sizeof a);        scanf("%d",&s);        for(int i=0; i<2*n; i++)            scanf("%d",&a[i]);        memset(dp,-1,sizeof dp);        for(int i=0; i<2*n; i++)            dp[i][0]=1;        int ans=slove(0,s);        if(ans==0)            printf("0\n");        else printf("1\n");    }}
原创粉丝点击