约数倍数选卡片 (蓝桥杯历年真题例题C++描述)递归博弈+优化 在官网已AC

原题链接:http://lx.lanqiao.cn/problem.page?gpid=T40

问题描述

  闲暇时,福尔摩斯和华生玩一个游戏:
  在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

 

解题思路:
   

    基本的博弈都可以用递归来求解,这道题由于想不到有效解法,则用 递归+优化

    博弈具有以下递归结构判断输赢:

    1. 尝试所有走法,把局面交给对手,如果对手的局面是必输,则当前局面必胜。(子结构)

    2. 尝试所有走法后(或不存在走法),还是赢不了,则当前局面必输。(边界)

    

    递归思路:

    dfs(局面s){

      for(尝试走一步s变成s1)

            把s1交给对手:dfs(s1) 如果存在一个s1局面必输:返回赢了;

      返回必输;

    }

   递归优化:递归优化的核心就是减少递归的子结构的层数

    不优化 只有50%成绩

    优化思路:1.优先当前可选的数字大的卡片(数字越大,则约数倍数的个数在100内越少,所以递归的层数就越少

                        2.常数优化,递归之前预处理每张卡片的下一次可选卡片集合

    还有一些不必要的优化,列如:优先考虑大的素数作为可选数字(在本题数字是100-1内,反而会使复杂度变高)

注意事项:

#include 
#include 
#include 
#include 
#include 
#define _for(i,a,b) for(int i=a;i=b;i--)

using namespace std;
typedef long long LL;
 
int a[111],b[111],c='A';
vector G[111];
int ri(){
    int x=0;
    while(!isdigit(c))if((c=getchar())=='\n')return 0;
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x;
}
int dp(int i){
    a[i]--;
    int len=G[i].size();
    _for(k,0,len){
        int j=G[i][k];
        if((!(i%j)||!(j%i))&&a[j]&&!dp(j)){a[i]++;return 1;}
    }
    a[i]++;
    return 0;
}
int main(){
    int x;
    while(x=ri()){
        a[x]++;
        if(c=='\n')break;
    }
    //预处理
    _for(i,1,101)
        _unfor(j,100,1)if((!(i%j)||!(j%i))&&a[j])
            G[i].push_back(j);
    //
    while(x=ri()){
        b[x]++;
        if(c=='\n')break;
    }
    _for(i,1,101)if(b[i])
        if(!dp(i)){printf("%d\n",i);return 0;}
    printf("%d\n",-1);
    return 0;
}

 

你可能感兴趣的:(蓝桥杯,ACM,算法,数据结构)