組合博弈

博弈,通常就是,sg啊,dfs極大極小搜索啊,dp啊,找規律啊....php

HDU 1847 Good Luck in CET-4 Everybody!(SG水題)node

預處理一下,否則會RE。ios

HDU 4559 塗色遊戲 ide

這個題,很是棒...看了別人的思路,n個1個格子的sg爲n%2,咱們利用sg函數處理出2*i的值,而後把給出的矩形分段。函數

#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
int dp[5001];
int p[3][5001];
int sg(int x)
{
    int i,l,r;
    int o[5001];
    if(dp[x] >= 0)
    return dp[x];
    memset(o,0,sizeof(o));
    for(i = 1;i <= x;i ++)
    {
        l = i-1;//染1*1的方格
        r = x-i;
        o[sg(l)^sg(r)^1] = 1;
        if(i+1 <= x)//枚舉在那裏染2*2的方格
        {
            l = i-1;
            r = x-i-1;
            o[sg(l)^sg(r)] = 1;
        }
    }
    for(i = 0;;i ++)
    {
        if(!o[i])
        return dp[x] = i;
    }
    return 0;
}
int main()
{
    int n,m,x,y,i,t,cas = 1,ans,flag;
    memset(dp,-1,sizeof(dp));
    dp[0] = 0;
    dp[1] = 0;
    for(i = 2;i <= 5000;i ++)
    {
        sg(i);
    }
    scanf("%d",&t);
    while(t--)
    {
        memset(p,0,sizeof(p));
        scanf("%d%d",&n,&m);
        for(i = 0;i < m;i ++)
        {
            scanf("%d%d",&x,&y);
            p[x][y] = 1;
        }
        ans = (2*n-m)&1;
        flag = 0;
        for(i = 1;i <= n;i ++)
        {
            if(!p[1][i]&&!p[2][i])
            flag ++;
            else
            {
                ans ^= dp[flag];
                flag = 0;
            }
        }
        ans ^= dp[flag];
        printf("Case %d: ",cas++);
        if(ans)
        printf("Alice\n");
        else
        printf("Bob\n");
    }
    return 0;
}
View Code

HDU 1760 A New Tetris Gamepost

極大極小dfsspa

#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
char str[50][50];
int p[50][50];
int n,m;
int dfs()
{
    int i,j,flag;
    flag = 0;
    for(i = 0;i < n-1;i ++)
    {
        for(j = 0;j < m-1;j ++)
        {
            if(p[i][j] == 0&&p[i+1][j] == 0&&p[i][j+1] == 0&&p[i+1][j+1] == 0)
            {
                p[i][j] = p[i+1][j] = p[i][j+1] = p[i+1][j+1] = 1;
                if(dfs() == 0)
                flag = 1;
                p[i][j] = p[i+1][j] = p[i][j+1] = p[i+1][j+1] = 0;
            }
        }
    }
    if(flag)
    return 1;
    else
    return 0;
}
int main()
{
    int i,j;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i = 0; i < n; i ++)
            scanf("%s",str[i]);
        for(i = 0; i < n; i ++)
        {
            for(j = 0; j < m; j ++)
            {
                if(str[i][j] == '0')
                p[i][j] = 0;
                else
                p[i][j] = 1;
            }
        }
        if(dfs())
        printf("Yes\n");
        else
        printf("No\n");
    }
    return 0;
}
View Code

HDU 3951 Coin Gamecode

這題先不考慮有環的狀況,長度爲n的sg會比較好求,暴力出sg會發現,除了k = 1以外,其餘狀況的k,dp[i] >= 1,不管第一步怎麼選,確定是要變化爲一段的狀況。blog

搜了一下題解,發現你們直接搞出了 策略,後手能夠搞出對稱的策略,用sg對不對啊,應該也沒問題吧...遊戲

#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
int dp[1001];
int n;
int sg(int x)
{
    int i,j;
    if(dp[x] >= 0)
    return dp[x];
    int o[1001];
    memset(o,0,sizeof(o));
    for(i = 1;i <= x;i ++)
    {
        for(j = 1;j <= n;j ++)
        {
            if(x-j-(i-1) < 0) continue;
            o[sg(i-1)^sg(x-j-(i-1))] = 1;
        }
    }
    for(i = 0;;i ++)
    if(!o[i])
    return dp[x] = i;
}
int main()
{
    int t,k,cas = 1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        printf("Case %d: ",cas++);
        if(k == 1)
        {
            if(n%2)
            printf("first\n");
            else
            printf("second\n");
        }
        else
        {
            if(n <= k)
            printf("first\n");
            else
            printf("second\n");
        }
    }
    return 0;
}
View Code

 HDU 3389 Game

徹底木有想法,看的題解,說這是階梯博弈...

這個題,1 3 4上的石子無法移動了,%3以後的變化0->0 1->2 2->1,其實原理也不是很懂, 打表或許是個好辦法。%6取餘以後,走到最後的奇偶性是肯定的。而後對奇數的狀況,異或一下,不懂啊...

#include <cstdio>
using namespace std;

int main()
{
    int t,cas = 1,i,x,ans,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        ans = 0;
        for(i = 1;i <= n;i ++)
        {
            scanf("%d",&x);
            if(i%6 == 0||i%6 == 2||i%6 == 5)
            ans ^= x;
        }
        printf("Case %d: ",cas++);
        if(ans)
        printf("Alice\n");
        else
        printf("Bob\n");
    }
    return 0;
}
View Code

HDU 1851  A Simple Game

還真是一個simple game,每一堆石子的sg(x)= x%(y+1),打個表看一下就好。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define LL long long
#define MOD 1000000009
int main()
{
    int i,t,n,x,y,ans;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        ans = 0;
        for(i = 0;i < n;i ++)
        {
            scanf("%d%d",&x,&y);
            ans ^= (x%(y+1));
        }
        if(ans)
        printf("No\n");
        else
        printf("Yes\n");
    }
    return 0;
}
View Code

 HDU 2873 Bomb Game 

和以前一個題 很相似,把遊戲劃分紅 在x,y上有炸彈,其餘地方都沒炸彈的狀況。求出sg值,而後把有炸彈的位置,異或一下。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define LL long long
#define MOD 1000000009
int dp[51][51];
int sg(int x,int y)
{
    int i,j;
    int o[1001];
    if(dp[x][y] >= 0)
        return dp[x][y];
    memset(o,0,sizeof(o));
    if(x > 0&&y > 0)
    {
        for(i = 0; i < x; i ++)
        {
            for(j = 0; j < y; j ++)
            o[sg(i,y)^sg(x,j)] = 1;
        }
    }
    else if(x == 0)
    {
        for(j = 0; j < y; j ++)
        o[sg(x,j)] = 1;
    }
    else
    {
        for(i = 0; i < x; i ++)
        {
            o[sg(i,y)] = 1;
        }
    }
    for(i = 0;;i ++)
    {
        if(!o[i])
        return dp[x][y] = i;
    }
}
int main()
{
    int i,j,n,m,ans;
    char str[51][51];
    memset(dp,-1,sizeof(dp));
    dp[0][0] = 0;
    sg(0,1);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(!n&&!m) break;
        ans = 0;
        for(i = 0; i < n; i ++)
        {
            scanf("%s",str[i]);
        }
        for(i = 0; i < n; i ++)
        {
            for(j = 0; j < m; j ++)
            {
                if(str[i][j] == '#')
                    ans ^= sg(i,j);
            }
        }
        if(ans)
            printf("John\n");
        else
            printf("Jack\n");
    }
    return 0;
}
View Code

 HDU 1907 John 

如下複製的。

這題與以往的博弈題的勝負條件不一樣,誰走完最後一步誰輸,但它也是一類NIM遊戲,記爲anti-nim遊戲
 
  首先給出結論:    先手勝當且僅當
 
(1)全部堆石子數都爲1且遊戲的SG值爲0 ,(2)存在某堆石子數大於1且遊戲的SG值不爲0
 
   證實:
 
(1)若全部堆石子數都爲1且SG值爲0,則共有偶數堆石子,故先手勝。
 
(2)
 
i)只有一堆石子數大於1時,咱們總能夠對該堆石子操做,使操做後石子堆數爲奇數且全部堆得石子數均爲1
 
ii)有超過一堆石子數大於1時,先手將SG值變爲0便可,且總還存在某堆石子數大於1
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define LL long long
int main()
{
    int t,i,j,n,x,flag,ans;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        flag = 1;
        ans = 0;
        for(i = 0;i < n;i ++)
        {
            scanf("%d",&x);
            ans ^= x;
            if(x != 1)
            flag = 0;
        }
        if(flag)
        {
            if(n%2 == 0)
            printf("John\n");
            else
            printf("Brother\n");
        }
        else
        {
            if(ans)
            printf("John\n");
            else
            printf("Brother\n");
        }
    }
    return 0;
}
View Code

HDU 3590 PP and QQ

這題是 anti-nim+樹的刪邊遊戲,葉子節點sg爲0,其餘的節點,爲兒子節點sg值+1異或起來。注意這題,一棵樹的sg值 可能會爲0啊!

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define LL long long
struct node
{
    int v,next;
}edge[301];
int first[301];
int flag[301];
int sg[301];
int t;
void add(int u,int v)
{
    edge[t].v = v;
    edge[t].next = first[u];
    first[u] = t ++;
}
void dfs(int x)
{
    int i,v,ans = 0;
    flag[x] = 1;
    for(i = first[x];i != -1;i = edge[i].next)
    {
        v = edge[i].v;
        if(flag[v]) continue;
        dfs(v);
        ans ^= (sg[v] + 1);
    }
    sg[x] = ans;
}
int main()
{
    int i,j,n,m,u,v,ans,z;
    while(scanf("%d",&n)!=EOF)
    {
        ans = 0;
        z = 0;
        for(i = 1;i <= n;i ++)
        {
            memset(first,-1,sizeof(first));
            memset(flag,0,sizeof(flag));
            t = 0;
            scanf("%d",&m);
            for(j = 1;j < m;j ++)
            {
                scanf("%d%d",&u,&v);
                add(u,v);
                add(v,u);
            }
            dfs(1);
            ans ^= sg[1];
            if(sg[1] > 1)
            z = 1;
        }
        if(z == 0)
        {
            if(ans)
            printf("QQ\n");
            else
            printf("PP\n");
        }
        else
        {
            if(ans)
            printf("PP\n");
            else
            printf("QQ\n");
        }
    }
    return 0;
}
View Code
相關文章
相關標籤/搜索