蓝桥杯真题——约数倍数选卡片(博弈论+DFS)

题目:

蓝桥杯真题——约数倍数选卡片(博弈论+DFS)_第1张图片

分析:

此题中输入的数字出现的次数可能不止一次,比如说输入为:1 2 2 3 3 4 5 (是题目中的另一个样例,在上述截图中未截出),那么我们可以使用一个num[105]数组来存放数字出现的次数,num数组存放的是第一行的数据,第二行输入数据表示先手可以第一次选择的数字,我们使用一个数组aa[105]来存放。对aa数组中的元素排序,之后利用循环来遍历aa数组中的每一个元素,表示先手可能先出的数字,在此基础上,利用DFS来判断后手可能出的数字,如果后手在这个过程中出现一次必胜态,那么先手此时就是必败的,在DFS中表现为return false,如果后手每一次都是必败的,那么先手就是必胜,表现为return true。

代码:
#include 
#include 
#include 
#include 
#include 
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;
}

你可能感兴趣的:(#,博弈论,算法)