【蓝桥杯】历届试题 约数倍数选卡片

问题描述

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

样例输入1

2 3 6
3 6

样例输出1

3

样例输入2

1 2 2 3 3 4 5
3 4 5

样例输出2

4

解题思路

1、题目中说明“当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方”,并且每次拿卡片都是依照同一规则,可以想到使用深度搜索。我们必须判断拿到最后一张牌才可以判定它是胜利还是失败。

2、两人轮流拿卡片,若其中一人拿到的是必胜的,显然另外一人无论拿到什么卡片都是必败的。因此在二人拿卡片的过程中,必胜卡片与必败卡片一定是交替出现,即上一张对方卡片为必胜的话,这一张我的一定为必败的,反之亦然。

3、如何进行深度搜索:所有拿卡片的规则都是统一的,即下一个人拿的数字一定是前一个人拿的数字的约数或倍数,因此对于每一个数字,当对方拿到这个数字时,我可以取到的数字是固定的。因此我们可以提前计算当取到每一张卡片时,下一张卡片的所有可能。在深度搜索时,只需要不断判断下一张卡片的所有可能性。

4、如何判断必胜还是必败:当对方选择了卡片A的情况下,①只要存在一个可能性我赢了,那么对方的A卡片都不是必胜的;②所有可能的情况下我都输了,那么对方的A卡片就是必胜的。因此,在判断卡片状态时,①如果某一可能性下我赢了,相反就可断定A卡片输了;②某一可能性下我输了,需要继续判断下一可能性,直至所有可能下我都输了,则判定对方A卡片胜。

//求出当对方取了x的情况下,我的状态 
//当dfs(x)=-1时,表示对方取了x的情况下,我是必输的,而对方的x卡片是必胜的 
int dfs(int x)
{
      
      //从前开始遍历时,两个测试点超时?? 
      for(int i=table[x].size()-1;i>=0;i--)
      {
     
           //当该可能性还存在于剩余数字中时 
           if(a[table[x][i]])
           {
     
                //假设已经取了这个数字 
                a[table[x][i]]--;
                int t=dfs(table[x][i]);
                a[table[x][i]]++;
                //若下一张i卡片是必胜的(t=-1),则返回1 至上一层 ,x卡片是必胜的 
                //若下一张i卡片是必败的(t=1), 并不能说明卡片x是必胜的,必须满足所有可能性都是必败才能得到x为必胜 
                if(t==-1)  return 1;   
            }
                //所有可取的卡片均没有了,则是必败的
                //所有可取且存在的卡片都是必胜的,则是必败的 
      } 
      return -1;
}
//求解过程简单来说就是可能数字不存在时,继续搜索下一可能数字;当t=-1时,返回上一层为1;当t=1时,继续搜索下一数字,直至循环至最后 

完整代码

#include
#include
#include
using namespace std;
int a[105]={
     0};
vector<int> choice;//题目给出可以选择的牌 
vector<int> table[105];//table[x]存储了当上一张选择x时,这一张可以选择的所有牌;table[x][i]即为当上一张选择x时,这一张可以选择的第i张牌 
int dfs(int x)
{
     
      for(int i=table[x].size()-1;i>=0;i--)
      {
     
          if(a[table[x][i]])
          {
     
              a[table[x][i]]--;
              int t=dfs(table[x][i]);
              a[table[x][i]]++;
              if(t==-1)  return 1;   
           }
       } 
      return -1;
}
int main()
{
     
      int x; 
      //输入所有数字 
      while(1)
      {
     
         cin>>x;
         a[x]++;
         char c=getchar();
         if(c=='\n')  break;
      }
     //输入可选择的数字 
     while(1)
     {
     
         cin>>x;
         choice.push_back(x);
         char c=getchar();
         if(c=='\n')  break;
     }
    //选中每个数字之后,下一步可以选什么
    for(int i=1;i<=100;i++)
    {
     
        if(a[i])
        {
     
            a[i]--;
            for(int j=1;j<=100;j++)
            {
     
                 //当还有数字j,并且满足为上一张牌的约束或倍数的要求 
                 if(a[j]&&(i%j==0||j%i==0))
                 table[i].push_back(j);
            }
           a[i]++;
        }
     } 
 //排序,当选多个数字都可以必胜时,输出其中最小的数字
    sort(choice.begin(),choice.end());
    for(int i=0;i<choice.size();i++)
    {
     
        a[choice[i]]--;
        int t=dfs(choice[i]);
        //!!确定选了第i张牌之后,抽到的牌时“-1”必败的,那选第i张牌就是必胜的 
        if(t==-1) 
        {
     
            cout<<choice[i];
            return 0;
        }
        a[choice[i]]++;
    }
    cout<<"-1";
    return 0;
}

你可能感兴趣的:(蓝桥杯,历届试题,数据结构)