• 蓝桥历年套题 约数倍数选卡片 博弈



    标题:约数倍数选卡片

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

    在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

      一开始感觉没啥思路,肯定是博弈,但sg表是打不了,也没有什么规律,然后看了下数据范围1~100,模拟了下就感觉可以直接暴力跑状态。

      首先要知道,必胜态和必败态的转换,首先如果一个状态先手能走到一个必败态的话那么它就是必胜态,反之,如果一个状态能走到的状态全是必胜态的话那么它就是必败态。

      最终的必败态肯定就是不能再选时,所以我们直接就是根据状态的转换,暴力深搜模拟所有可能的情况,得到每一步的状态,然后逆推出一开始的状态是必胜还是必败。思路就是这样,很简单,但很容易超时,有两个解决超时的优化,第一个是在读入上用sting流读入,这个c++课一开始就学了,不懂的可以去了解一下。第二个就是在每个数下一步可以走到的数的处理上,我们类似建图一样建边,然后让大的数在前面,这样每次走的时候先走大的数。(这里我也不知道为什么,我一开始从小到大就TLE了,个人觉得可能是一个数的因子数是比它的倍数少,能走到的状态更少,更快走到底),剩下的就是详情见代码了。  

     1 #include<cstdio>
     2 #include<sstream>
     3 #include<iostream>
     4 #include<vector>
     5 #include<algorithm>
     6 using namespace std;
     7 const int N=118;
     8 struct Side{
     9     int v,ne;
    10 }S[N<<2];
    11 int sn,num[N],head[N];
    12 vector<int> v;
    13 void init()
    14 {
    15     sn=0;
    16     for(int i=1;i<=100;i++)
    17     {
    18         head[i]=-1;
    19         num[i]=0;
    20     }
    21 }
    22 void add(int u,int v)
    23 {
    24     S[sn].v=v;
    25     S[sn].ne=head[u];
    26     head[u]=sn++;
    27 }
    28 bool check(int x)//check(x)x取掉之后下一个人的先手状态
    29 {//return 1的话,当前做决定的人必败,所以拿掉x那个是必胜的
    30 //return 0的话,当前做决定的人必胜,所以拿掉x那个是必败的
    31     for(int i=head[x],j;~i;i=S[i].ne)
    32     {
    33         j=S[i].v;
    34         if(num[j])
    35         {
    36             num[j]--;
    37             if(check(j))//取完j后是必胜态的话,
    38             {//对于当前取完x状态来说就是必败态 
    39                 num[j]++;
    40                 return 0; 
    41             }
    42             num[j]++;
    43         }
    44     }
    45     return 1;//当前状态不能再选时,上一个就是必胜态
    46 }
    47 int main()
    48 {
    49     init();
    50     int x;
    51     string s;
    52     getline(cin,s);
    53     stringstream sina(s);
    54     while(sina>>x)//string流读入 
    55         num[x]++;//统计每个数的个数
    56     //前向星建图是头插法,所以从小到大建 
    57     for(int i=1;i<=100;i++)
    58     {
    59         if(num[i])
    60         {
    61             for(int j=i;j<=100;j++)
    62                 if(num[j]&&(i%j==0||j%i==0))
    63                 {//如果i和j都存在,而且彼此是约数或者倍数就建边 
    64                     add(i,j);
    65                     if(i!=j)//避免自己到自己建两条边 
    66                         add(j,i);
    67                 }
    68         }
    69     }
    70     getline(cin,s);
    71     stringstream sinb(s);
    72     while(sinb>>x)
    73         v.push_back(x);
    74     sort(v.begin(),v.end());
    75     int ans=-1;
    76     for(int i=0;i<v.size();i++)
    77     {
    78         if(i&&v[i]==v[i-1])//去重 
    79             continue;    
    80         num[v[i]]--;//从剩余卡牌中去掉一张当前的数的卡牌 
    81         if(check(v[i]))//dfs爆搜这种情况的状态 
    82         {
    83             ans=v[i];
    84             break;
    85         }
    86         num[v[i]]++;
    87     }
    88     printf("%d
    ",ans);
    89     return 0;
    90 }
    必胜必败
  • 相关阅读:
    转载~基于比较的排序算法的最优下界为什么是O(nlogn)
    关于 cgdb & gdbtui 的输入scanf()问题
    制定ip池内随机生成ip地址
    C 随机不重复元素~转
    随机选取算法 (有权重的记录中选取)~转
    全局变量的教训
    Python字符串的encode与decode研究心得——解决乱码问题
    Python 求最大公因式~辗转相除法
    Python格式化字符串~转
    Python 中的枚举类型~转
  • 原文地址:https://www.cnblogs.com/LMCC1108/p/10896950.html
Copyright © 2020-2023  润新知