蓝桥杯历届试题——约数倍数选卡片(博弈论+dfs)

问题描述
  闲暇时,福尔摩斯和华生玩一个游戏:
  在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
  1,2,3, 6,12,18,24 ….
  当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
  请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
  当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。
输入格式
  输入数据为2行。第一行是若干空格分开的整数(每个整数介于1~100间),表示当前剩余的所有卡片。
  第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。
输出格式
  程序则输出必胜的招法!!
样例输入
2 3 6
3 6
样例输出
3
样例输入
1 2 2 3 3 4 5
3 4 5
样例输出
4

博弈论果然还是很难
总之分析下必胜态和必败态,如果我拿了一张牌,并且没有其他的约数或倍数可拿,显然这是我的必胜态。如果在这之前,对方也拿了一张牌,显然对他来说他拿的那张牌就是必败态,所以这种博弈里,必胜态和必败态是交替出现的
但要确定是必败还是必胜,不抽到最后一张还是不能确定,所以要把所有可能的抽卡情况搜索出来,只有确定抽了这张牌,之后所有的情况都是必败,我才能确定这张牌是必胜

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define INF 0x3f3f3f3f
#define MAXN 100005
#define Mod 1000000007
using namespace std;
int num[200];
vector<int> choice;
vector<int> table[200];
int dfs(int x) //必败态和必胜态轮流出现,所以要找取第一个x时到底是必胜还是必败
{
    for(int i=table[x].size()-1;i>=0;--i) //从大到小取数,不然会超时,想不出为什么
    {
        if(num[table[x][i]])
        {
            num[table[x][i]]--;
            int t=dfs(table[x][i]);
            num[table[x][i]]++;
            if(t==-1) //如果下一个状态是必胜态
                return 1; //那当前状态就是必败态,并且容易看出从第一个dfs状态看来,只要后面有一个必胜态,那结果就一定是必败
        }
    }
    return -1; //没有下一个约数或倍数了,取到这个x的人必胜
}
int main()
{
    memset(num,0,sizeof(num));
    int x;
    while(1)
    {
        scanf("%d",&x);
        num[x]++;
        char c=getchar();
        if(c=='\n')
            break;
    }
    while(1)
    {
        scanf("%d",&x);
        choice.push_back(x);
        char c=getchar();
        if(c=='\n')
            break;
    }
    for(int i=1; i<=100; ++i)
        if(num[i])
        {
            num[i]--;
            for(int j=1; j<=100; ++j)
                if(num[j]&&(i%j==0||j%i==0))
                    table[i].push_back(j);
            num[i]++;
        }
    sort(choice.begin(),choice.end());
    int flag=0;
    for(int i=0;iint t=dfs(choice[i]);
        if(t==-1)
        {
            printf("%d\n",choice[i]);
            flag=1;
            break;
        }
        num[choice[i]]++;
    }
    if(!flag)
        printf("-1\n");
    return 0;
}

你可能感兴趣的:(博弈论,搜索)