题目:
分析:
此题中输入的数字出现的次数可能不止一次,比如说输入为:1 2 2 3 3 4 5 (是题目中的另一个样例,在上述截图中未截出),那么我们可以使用一个num[105]数组来存放数字出现的次数,num数组存放的是第一行的数据,第二行输入数据表示先手可以第一次选择的数字,我们使用一个数组aa[105]来存放。对aa数组中的元素排序,之后利用循环来遍历aa数组中的每一个元素,表示先手可能先出的数字,在此基础上,利用DFS来判断后手可能出的数字,如果后手在这个过程中出现一次必胜态,那么先手此时就是必败的,在DFS中表现为return false,如果后手每一次都是必败的,那么先手就是必胜,表现为return true。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 105;
//num数组用于存放第一行输入的每一个数的出现次数 aa数组用于存放第二行先手可以先出的牌数
int num[MAXN],aa[MAXN];
vector<int> vec[MAXN]; //vec[i]用于存放在1和100之间并且在num数组中出现过的i的约数或者倍数
int ind;
bool Select(int k)
{
//这里必须要从vec[k].size()开始,如果从0开始会出现超时(这里不懂为啥超时。。。)
for(int i=vec[k].size()-1;i>=0;--i)
if(num[vec[k][i]])
{
num[vec[k][i]]--;
bool ok = Select(vec[k][i]);
num[vec[k][i]]++;
if(ok) return false;
}
return true;
}
int main()
{
int x;
while(true)
{
cin >> x;
num[x]++;
char ch = getchar();
if(ch == '\n') break;
}
while(true)
{
cin >> x;
aa[++ind] = x;
char ch = getchar();
if(ch == '\n') break;
}
//对aa数组排序,因为如果先手有必胜态,题目要求的是输出能产生必胜态的最小的数字
sort(aa+1,aa+ind+1);
//下面的循环两层必须都从1到100循环,因为不管是先手还是后手,拿到一张牌之后,都要依据vec来
//寻找此数的约数或是倍数。
for(int i=1;i<=100;++i)
for(int j=1;j<=100;++j)
if((i%j==0||j%i==0)&&num[j])
vec[i].push_back(j);
for(int i=1;i<=ind;++i)
{
if(num[aa[i]])
{
num[aa[i]]--; //此处表示先出首先出了aa[i]这张牌
//如果Select(aa[i])返回true,那么说明后手不管出了什么牌,都是必败的,那么此时就可以输出
//aa[i]并且结束程序
if(Select(aa[i]))
{
printf("%d\n",aa[i]);
return 0;
}
num[aa[i]]++; //回溯 这里表示先出aa[i]是必败的,那么判断下一种情况
}
}
printf("-1\n");
return 0;
}