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

题目描述
闲暇时,福尔摩斯和华生玩一个游戏:

在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

资源约定:
峰值内存消耗 < 64M
CPU消耗 < 1000ms

思路:n最大为100,先用一个vector数组预处理出所有能选的牌,然后对第一步能出的所有牌排序后遍历,进行搜索,若有牌可以赢,直接输出并break。
注意:输入的时候用到了stringstream来处理字符串(头文件是ssstream),具体的可以百度一下。

#include
#include
#include
#include
using namespace std;
#define N 100005
int num[N];
int n;
vector<int> b;
vector<int> a[105];
int dfs(int x)    //在上一张选x的情况下
{
	for(int i=a[x].size()-1;i>=0;i--) //遍历选了x之后能选的牌
	{
		if(num[a[x][i]])
		{
			num[a[x][i]]--;
			int t=dfs(a[x][i]);   //若我选了这张牌以后对方会输,返回1(自己可以赢)
			num[a[x][i]]++;
			if(t==-1)
				return 1;
		}
	}
	return -1;  //若无牌可选或者选任一能选的牌之后对方都会赢,返回-1(自己会输)
}
int main()
{
    string s;
    int x;
    getline(cin,s);
    stringstream in(s);
    while(in>>x)
    {
        num[x]++;
    }
    int ans=-1;
    getline(cin,s);
    stringstream iin(s);
    while(iin>>x)
    {
        b.push_back(x);
    }  
    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))
                    a[i].push_back(j);
            }
            num[i]++;
        }
    } 
    sort(b.begin(),b.end());  //先排序再遍历,因为如果有两张牌都能赢,需输出小的那个
    for(int i=0;i<b.size();i++)
    {
        num[b[i]]--;
        int t=dfs(b[i]);
        if(t==-1)
        {
            cout<<b[i]<<endl;
            return 0;
        }
        num[b[i]]++;
    }
    cout<<"-1"<<endl;
    return 0;
}

你可能感兴趣的:(蓝桥杯,博弈)