组合博弈

博弈,一般就是,sg啊,dfs极大极小搜索啊,dp啊,找规律啊....

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

预处理一下,不然会RE。

HDU 4559 涂色游戏 

这个题,非常棒...看了别人的思路,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 Game

极大极小dfs

#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 Game

这题先不考虑有环的情况,长度为n的sg会比较好求,暴力出sg会发现,除了k = 1之外,其他情况的k,dp[i] >= 1,无论第一步怎么选,肯定是要变化为一段的情况。

搜了一下题解,发现大家直接搞出了 策略,后手可以搞出对称的策略,用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

 

 

 

 

你可能感兴趣的:(组合)