• 博弈论嘻嘻


    https://www.cnblogs.com/Simon-X/p/5905960.html 

    这个介绍得很好,生动形象得介绍了sg。

    http://hihocoder.com/contest/hiho46/problem/1 这个呢就是官方点得解释,emmm我选择上面那个

    chess hdu 5724

    题意:有一个n行20列的棋盘,棋盘上分布着一些棋子,A、B两人轮流下棋,A先手,每次操作可以将某个棋子放到自己右边的第一个空位(也就是说右边如果已经有子,可以跳过它,没有就右移一步),但最多20列,绝对不能超过棋盘,无棋可走的输。

    题解:进行状态压缩,bit来表示在一行中一个点有没有棋子,有棋子为1,没有棋子为0,0到(2^20-1)就代表全了所有的可能。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
     
    using namespace std;
     
    int SG[(1<<20)+100],book[30];
     
    void get_SG()
    {
        for(int i=0;i<(1<<20);i++)
        {
            memset(book,-1,sizeof(book));
     
            int last=-1;
            for(int j=0;j<20;j++)
            {
                if(!((i>>j)&1)) ///空格在最右的位置
                    last=j;
                if((i>>j)&1)   ///最右棋子的位置
                {
                    if(last!=-1)
                        book[SG[(i^(1<<j))^(1<<last)]]=true; ///后继状态标记
                ///找到最右边的棋子以及可移动的空格,然后互换成后继并标记,互换后一定比互换前的值小,因为我们是先找空格再找棋子的
                ///
                }
            }
     
            int item=0;
            while(book[item]!=-1) item++; ///找出最小的不属于这个集合的非负整数
     
    //        printf("item=%d\n",item);
            SG[i]=item;
        }
    }
    int main()
    {
        memset(SG,0,sizeof(SG));
        get_SG();
     
     
        int ncase;
        scanf("%d",&ncase);
     
        while(ncase--)
        {
            int n,m,ans=0,item;
     
            scanf("%d",&n);
     
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&m);
                item=0;
     
                for(int j=1;j<=m;j++){
                    int x;
                    scanf("%d",&x);
     
                    item^=1<<(20-x);
                }
    //            printf("%d\n",SG[item]);
                ans^=SG[item];
            }
    //    printf("ans=%d\n",ans);
            if(ans) printf("YES\n");
            else printf("NO\n");
        }
     
        return 0;
    }
    

    Doubloon Game

    题意:给你SS个石子和一个数字kk,每次只能取kk的幂次的石子数,如:1,k,k2...1,k,k2...,谁先取完谁赢,问你最少取多少个可以获得胜利,即可以使对手面临必败面。

    第一开始是直接想直接暴力4求出sg值,但是范围是1e9,所以不可以直接求打表

    那么,想想就知道打个找下规律了,我们发现

     当k为奇数,则010101分布

    当k为偶数,则有个循环节,那么直接判断即可

    #include<bits/stdc++.h>
    using namespace std;
    int sg[1010],book[1010];
    int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
        return x*f;
    }
    void getsg()
    {
        sg[0]=0;
        for(int i=1;i<=1000;i++)
        {
            memset(book,0,sizeof(book));
            for(int j=1;j<=i;j*=6)
                book[sg[i-j]]=1;
            for(int j=0;;j++)
            if(!book[j])
            {
                sg[i]=j;
                break;
            }
        }
    }
    
    int main()
    {
        int t;t=read();
        while(t--)
        {
            int n=read(),k=read();
            if(k%2)
            {
                if(n%2) puts("1");
                else puts("0");
            }
            else
            {
                int c=n%(k+1);
                if(c<k)
                {
                    if(c%2) puts("1");
                    else puts("0");
                }
                else
                {
                    int flag=0;
                    for(int j=k;j<=n;j*=k)
                    {
                        int x=n-j;
                        c=x%(k+1);
                        if(!(c%2))
                        {
                            flag=j;
                            break;
                        }
                    }
                    if(flag) printf("%d\n",flag);
                    else puts ("0");
                }
            }
        }
        return 0;
    }
    

    mine

    扫雷游戏,点开一个方,如果空白,则将周围8格的数字和空白翻开,如果过程中翻开空格,则继续由该空格翻开周围8格(显然dfs嘛)点击空格方格,空格方格消失并且每个空方格连接的数字方格也消失,问你和对手已经事先知道所有的雷在哪,

    谁第一个碰到雷谁gg。

    在一盘游戏

    中,一个格子不可能被翻开两次,说明任意两块空地不会包含相同的格子。

    这样我们可以把一大块空白区域当作一堆石子,单独的数字就当作一块石子。

    就可以分为多个小游戏了。

    当空地旁边没连任何数字的时候,sg = 1(直接转移到 0)。如果有一个

    数字,点空地可以转移到 0,点数字可以转移到 1,所以 sg = 2。有 2 个数

    字点空地转移到 0,点数字转移到 2,所以 sg = 1。

    以此类推,空地旁边有奇数个数字的时候,sg = 2,否则 sg = 1。

    剩下的没与空地相连的数字,每个的 sg 都是 1。

    那么将所有空地的 sg 异或起来,再异或 (不与空地相连的数字个数对 2

    取模),等于零输出后手赢,大于 0 输出先手赢即可。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #define MP make_pair
    using namespace std;
    typedef pair<int ,int > P;
    queue<P>que;
    const int maxx = 1e3+10;
    int dx[]={0,0,-1,-1,-1,1,1,1};
    int dy[]={1,-1,0,-1,1,0,1,-1};
    int mp[maxx][maxx];
    bool book[maxx][maxx];
    
    int n,m,k;
    int read()
    {
        int f=1,x=0; char c=getchar();
        while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
        while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
        return x*f;
    }
    bool docheck(int x,int y){
        if(x<0||x>=n||y<0||y>=m||mp[x][y]==-1) return false;
        return true;
    }
    
    int bfs(int x,int y){
        que.push(MP(x,y));
        book[x][y]=1;
        int num=1;
        while(!que.empty()){
            P top = que.front();
            que.pop();
            for(int i=0;i<8;i++){
                int tmpx = top.first+dx[i];
                int tmpy = top.second+dy[i];
                if(!docheck(tmpx,tmpy)||book[tmpx][tmpy]) continue;
                if(mp[tmpx][tmpy]>0) num++;
                if(mp[tmpx][tmpy]==0) que.push(MP(tmpx,tmpy));
                book[tmpx][tmpy]=1;
            }
        }
        return num;
    }
    
    int main()
    {
        int T,p=0;
    //    T=read();
        scanf("%d",&T);
        while(T--){
    //        int n=read(),m=read(),k=read();
           scanf("%d%d%d",&n,&m,&k);
            memset(book,0,sizeof(book));
            memset(mp,0,sizeof(mp));
            for(int i=0;i<k;i++){
                int x,y;
                scanf("%d%d",&x,&y);
                mp[x][y]=-1;
                for(int j=0;j<8;j++){
                    int tmpx=x+dx[j];
                    int tmpy=y+dy[j];
                    if(docheck(tmpx,tmpy)) mp[tmpx][tmpy]=1;
                }
            }
    
            int ans = 0;
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    if(mp[i][j]==0&&!book[i][j])
                        ans^=2-bfs(i,j)%2;
                }
            }
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++)
                    if(mp[i][j]==1&&!book[i][j])
                        ans^=1;
            }
            printf("Case #%d: ",++p);
            if(ans) puts("Xiemao");
            else puts("Fanglaoshi");
        }
        return 0;
    }
    

    HDU1404-sg

    意思就是给你长度不大于6的数字

    然后你和对手两个操作,要么把该数字例如3变得一个更小的数字,2 1 0,要么选择一个数字为0的,然后把0左边包括这个0一起删掉,最后把所有数字都删除的获胜

    题解:

    1是必败点那么所有被操作成1的数都是必胜点,以此类推由必败点按找游戏的规则反方向推出所有的必胜点。

    反向推出好像之前也有一道,我也是不会嘻嘻嘻。

    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <string.h>
    #include <map>
    #include <vector>
    #include <cstdlib>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    #include <set>
    #include <stack>
    #include <functional>
    #include <fstream>
    #include <sstream>
    #include <iomanip>
    #include <numeric>
    #include <cassert>
    #include <bitset>
    #include <stack>
    #include <ctime>
    #include <list>
    #define INF 0x7fffffff
    #define max3(a,b,c) (max(a,b)>c?max(a,b):c)
    #define min3(a,b,c) (min(a,b)<c?min(a,b):c)
    #define mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
     
    bool sg[1000000];
     
    int get_length(int n)//得到整数n的位数
    {
        if(n/100000) return 6;
        if(n/10000) return 5;
        if(n/1000)  return 4;
        if(n/100)   return 3;
        if(n/10)  return 2;
        return 1;
    }
     
    void Deal(int n)
    {
        int m, i, j, base, len, t;
        len = get_length(n);
        for(i = 1; i <= len; i++) //对每一位上加上一个数,例如1234 是必败,那么1235 1236 ...1239必胜,还有1244.1254.....1294,以此类推
        {
            m = n;
            base = pow(10,i-1);
            t = (m%(base*10))/base;
            for(j = t; j < 9; j++){
                m += base;
                sg[m] = true;
            }
        }
        m = n;
        base = 1;
        for(i = len; i < 6; i++) //后面加0开头的数
        {
            m *= 10;
            for(j = 0; j < base; j++)
                sg[m+j] = true;
            base *= 10;
        }
    }
    void Init()
    {
        memset(sg,false,sizeof(sg));
        int i;
        for(i=1; i<1000000; i++) //由必败点找出所有的必胜点
            if(!sg[i])
                Deal(i);
    }
    int main()
    {
        string s;
        int i,sum;
        Init();
        while(cin>>s)
        {
            if(s[0]=='0')//0开头的都是必胜的
            {
                printf("Yes\n");
                continue;
            }
            sum=0;
            for(i=0; i<s.size(); i++) //字符串转变成整型
                sum=sum*10+s[i]-'0';
            if(sg[sum])
                printf("Yes\n");
            else
                printf("No\n");
        }
        return 0;
    }
    
  • 相关阅读:
    hdu1561 The more, The Better 树形DP+分组背包
    hdu1520 Anniversary party 简单树形DP
    hdu1054 Strategic Game 树形DP
    hdu1011 Starship Troopers 树形DP
    hdu4681 String DP(2013多校第8场)
    zoj3469 Food Delivery 区间DP
    LightOJ 1422 -Halloween Costumes 区间DP
    hdu4283 You Are the One 区间DP
    poj1651 Multiplication Puzzle 区间DP
    codeforce 149D Coloring Brackets 区间DP
  • 原文地址:https://www.cnblogs.com/hgangang/p/11806521.html
Copyright © 2020-2023  润新知