zoukankan      html  css  js  c++  java
  • hdu1536&&hdu3023 SG函数模板及其运用

    Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u

     Status

    Description

    Arthur and his sister Caroll have been playing a game called Nim for some time now. Nim is played as follows: 


      The starting position has a number of heaps, all containing some, not necessarily equal, number of beads. 

      The players take turns chosing a heap and removing a positive number of beads from it. 

      The first player not able to make a move, loses. 


    Arthur and Caroll really enjoyed playing this simple game until they recently learned an easy way to always be able to find the best move: 


      Xor the number of beads in the heaps in the current position (i.e. if we have 2, 4 and 7 the xor-sum will be 1 as 2 xor 4 xor 7 = 1). 

      If the xor-sum is 0, too bad, you will lose. 

      Otherwise, move such that the xor-sum becomes 0. This is always possible. 


    It is quite easy to convince oneself that this works. Consider these facts: 

      The player that takes the last bead wins. 

      After the winning player's last move the xor-sum will be 0. 

      The xor-sum will change after every move. 


    Which means that if you make sure that the xor-sum always is 0 when you have made your move, your opponent will never be able to win, and, thus, you will win. 

    Understandibly it is no fun to play a game when both players know how to play perfectly (ignorance is bliss). Fourtunately, Arthur and Caroll soon came up with a similar game, S-Nim, that seemed to solve this problem. Each player is now only allowed to remove a number of beads in some predefined set S, e.g. if we have S =(2, 5) each player is only allowed to remove 2 or 5 beads. Now it is not always possible to make the xor-sum 0 and, thus, the strategy above is useless. Or is it? 

    your job is to write a program that determines if a position of S-Nim is a losing or a winning position. A position is a winning position if there is at least one move to a losing position. A position is a losing position if there are no moves to a losing position. This means, as expected, that a position with no legal moves is a losing position.
     

    Input

    Input consists of a number of test cases. For each test case: The first line contains a number k (0 < k ≤ 100 describing the size of S, followed by k numbers si (0 < si ≤ 10000) describing S. The second line contains a number m (0 < m ≤ 100) describing the number of positions to evaluate. The next m lines each contain a number l (0 < l ≤ 100) describing the number of heaps and l numbers hi (0 ≤ hi ≤ 10000) describing the number of beads in the heaps. The last test case is followed by a 0 on a line of its own.
     

    Output

    For each position: If the described position is a winning position print a 'W'.If the described position is a losing position print an 'L'. Print a newline after each test case. 
     

    Sample Input

    2 2 5
    3
    2 5 12
    3 2 4 7
    4 2 3 7 12
    5 1 2 3 4 5
    3
    2 5 12
    3 2 4 7
    4 2 3 7 12
    0

    Sample Output

    LWW WWL
     

    Source

    Norgesmesterskapet 2004
    此题题意sg问题的入门题,第一行第一个数代表着s集合的个数,s集合代表着我们一次可以取得物品的个数,后面紧跟着k个数
    第二行有一个数代表着询问次数,
    接下面每行第一行有一个数代表着总的堆数n,后面紧跟着n个数,分别代表每个堆物品的数量,
    问谁先取光谁获胜,
    典型的SG问题,
    下面附有SG函数的模板
    1打表模板
    //f[]:可以取走的石子个数
    //sg[]:0~n的SG函数值
    //hash[]:mex{}
    int f[N],sg[N],hash[N];     
    void getSG(int n)
    {
        int i,j;
        memset(sg,0,sizeof(sg));
        for(i=1;i<=n;i++)
        {
            memset(hash,0,sizeof(hash));
            for(j=1;f[j]<=i;j++)
                hash[sg[i-f[j]]]=1;
            for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数
            {
                if(hash[j]==0)
                {
                    sg[i]=j;
                    break;
                }
            }
        }
    }

    2dfs模板

    dfs传入的参数x为每堆得数量,所以根据题意假如有n堆物品,那么就要进行n次传参就行dfs;

    其实sg数组应该在主函数中进行初始化,

    我们每次都dfs下一个状态,也就是当前的x状态经过f数组中其中一个取物品方法而得到的另外一种状态,队友回溯的时候我们找到没有被vis标记值

    也就是mes集合里面后继点中未出现的最小的正整数值

    //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
    //n是集合s的大小 S[i]是定义的特殊取法规则的数组
    int s[110],sg[10010],n;
    int SG_dfs(int x)
    {
        int i;
        if(sg[x]!=-1)
            return sg[x];
        bool vis[110];
        memset(vis,0,sizeof(vis));
        for(i=0;i<n;i++)
        {
            if(x>=s[i])
            {
                SG_dfs(x-s[i]);
                vis[sg[x-s[i]]]=1;
            }
        }
        int e;
        for(i=0;;i++)
            if(!vis[i])
            {
                e=i;
                break;
            }
        return sg[x]=e;
    }

    下面附上hdu1536的代码

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
    //n是集合s的大小 S[i]是定义的特殊取法规则的数组
    int s[110],sg[10010],n;
    int SG_dfs(int x){
        int i;
        if(sg[x]!=-1)
            return sg[x];
        bool vis[110];
        memset(vis,0,sizeof(vis));
        for(i=0;i<n;i++){
            if(x>=s[i])
            {
                SG_dfs(x-s[i]);
                vis[sg[x-s[i]]]=1;
            }
        }
        int e;
        for(i=0;;i++)
            if(!vis[i])
            {
                e=i;
                break;
            }
        return sg[x]=e;
    }
    int main()
    {
        int i,m,t,num;
        while(scanf("%d",&n)&&n)
        {
            for(i=0;i<n;i++)
                scanf("%d",&s[i]);
            memset(sg,-1,sizeof(sg));
            sort(s,s+n);
            scanf("%d",&m);
            while(m--)
            {
                scanf("%d",&t);
                int ans=0;
                while(t--)
                {
                    scanf("%d",&num);
                    ans^=SG_dfs(num);
                }
                if(ans==0)
                    printf("L");
                else
                    printf("W");
            }
            printf("
    ");
        }
        return 0;
    }

    下面同hdu3032

    Nim or not Nim?

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


    Problem Description
    Nim is a two-player mathematic game of strategy in which players take turns removing objects from distinct heaps. On each turn, a player must remove at least one object, and may remove any number of objects provided they all come from the same heap.

    Nim is usually played as a misere game, in which the player to take the last object loses. Nim can also be played as a normal play game, which means that the person who makes the last move (i.e., who takes the last object) wins. This is called normal play because most games follow this convention, even though Nim usually does not.

    Alice and Bob is tired of playing Nim under the standard rule, so they make a difference by also allowing the player to separate one of the heaps into two smaller ones. That is, each turn the player may either remove any number of objects from a heap or separate a heap into two smaller ones, and the one who takes the last object wins.
     
    Input
    Input contains multiple test cases. The first line is an integer 1 ≤ T ≤ 100, the number of test cases. Each case begins with an integer N, indicating the number of the heaps, the next line contains N integers s[0], s[1], ...., s[N-1], representing heaps with s[0], s[1], ..., s[N-1] objects respectively.(1 ≤ N ≤ 10^6, 1 ≤ S[i] ≤ 2^31 - 1)
     
    Output
    For each test case, output a line which contains either "Alice" or "Bob", which is the winner of this game. Alice will play first. You may asume they never make mistakes.
     
    Sample Input
    2
    3
    2 2 3
    2
    3 3
     
    Sample Output
    Alice Bob
     
    Source
     
    Recommend
    第一行输入一个正整数t代表case
    第二行代表有n个堆,、
    下面n个整数代表每堆中物品的数量
    题目的要求的可以有两种操作,一种是从一个堆中取走任意数量的物品,第二种就是将一堆分成两个小堆

    思路: 此题为博弈中的—取走-分割游戏(这种游戏允许取走某些东西,然后将原来的一个游戏分成若干个相同的游戏)

    由于数据范围,不能直接求sg值只能打表找规律;

    Lasker's Nim游戏:每一轮允许两会中操作之一:①、从一堆石子中取走任意多个,②、将一堆数量不少于2的石子分成都不为空的两堆。

    分析:很明显:sg(0) = 0,sg(1) = 1。

    状态2的后继有:0,1和(1,1),他们的SG值分别为0,1,0,所以sg(2) =2。

    状态3的后继有:0、1、2、(1,2),他们的SG值分别为0、1、2、3,所以sg(3) = 4。

    状态4的后继有:0、1、2、3、(1,3)和(2,2),他们的SG值分别为0,1,2,4,5,0,所以sg(4) = 3.

    再推一些,推测得到:对于所有的k >= 0,有 sg( 4k+1 ) = 4k+1; sg( 4k+2 ) = 4k+2; sg( 4k+3 ) = 4k+4; sg( 4k+4 ) = 4k+3。

    假设游戏初始时有3堆,分别有2、5和7颗石子。三堆的SG函数值分别为2、5、8,他们的Nim和等于15.所以要走到P状态,就要使得第三堆的SG值变成7,可以将第三对按1和6分成两堆。

    下面附上打表代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    
    using namespace std;
    
    const int N=1000010;
    
    int sg[N];
    
    int g(int x){
        int mex[1010];
        memset(mex,0,sizeof(mex));
        if(sg[x]!=-1)
            return sg[x];
        for(int i=x-1;i>=0;i--)//在进入一种状态的时候,我们可以取走任意多的数量,但是不能不取,所以从x-1到0这些情况的mex【g(x)】都应该标记一下
            mex[g(i)]=1;
        for(int i=1;i<=x/2;i++){
            int ans=0;
            ans^=g(i);//同样一堆我们应该分成两堆,对这两队进行在一起dfs
            ans^=g(x-i);
            mex[ans]=1;
        }
        for(int i=0;;i++)
            if(!mex[i])
                return sg[x]=i;
    }
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
    
        int t,n;
        scanf("%d",&t);
        memset(sg,-1,sizeof(sg));
        while(t--){
            scanf("%d",&n);
            int x;
            for(int i=0;i<n;i++){
                scanf("%d",&x);
                g(x);
                printf("sg[%d]=%d
    ",x,sg[x]);
            }
            for(int i=0;i<=100;i++){
                printf("%d ",sg[i]);
                //if(i%10==0)
                    //system("pause");
            }
            printf("
    ");
        }
        return 0;
    }

    通过找规律我们可以总结出下面的代码

    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int main(){
       int t;
        scanf("%d",&t);
        while(t--){
           int ans=0;
            int n;
            scanf("%d",&n);
            int x;
            for(int i=0;i<n;i++){
                scanf("%d",&x);
                if(x%4==1||x%4==2)
                ans^=x;
                else if(x%4==3)
                ans^=(x+1);
                else
                ans^=(x-1);
            }
            if(ans==0)
            printf("Bob
    ");
            else
            printf("Alice
    ");
        }
        return 0;
    }
  • 相关阅读:
    Beta版软件说明书
    团队项目第五天
    cnblogs用户体验评价
    团队项目测试计划
    Alpha版总结会议
    软件使用说明书
    团队绩效考核
    各个小组对于“我爱淘”的评价
    软件工程团队项目评价
    丹佛机场行李系统处理方案
  • 原文地址:https://www.cnblogs.com/13224ACMer/p/5155302.html
Copyright © 2011-2022 走看看