标签:简单 cst 直接 algorithm 数论 标题 size 思路 col
标题:约数倍数选卡片
闲暇时,福尔摩斯和华生玩一个游戏:
在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)//1先手必胜 0先手必败 29 { 30 for(int i=head[x],j;~i;i=S[i].ne) 31 { 32 j=S[i].v; 33 if(num[j]) 34 { 35 num[j]--; 36 if(check(j))//如果走到了一个必胜态 37 { 38 num[j]++; 39 return 0;//当前状态就是必败态 40 } 41 num[j]++; 42 } 43 } 44 return 1;//不能再选时,当前状态就是必胜态 45 } 46 int main() 47 { 48 init(); 49 int x; 50 string s; 51 getline(cin,s); 52 stringstream sina(s); 53 while(sina>>x)//string流读入 54 num[x]++;//统计每个数的个数 55 //前向星建图是头插法,所以从小到大建 56 for(int i=1;i<=100;i++) 57 { 58 if(num[i]) 59 { 60 for(int j=i;j<=100;j++) 61 if(num[j]&&(i%j==0||j%i==0)) 62 {//如果i和j都存在,而且彼此是约数或者倍数就建边 63 add(i,j); 64 if(i!=j)//避免自己到自己建两条边 65 add(j,i); 66 } 67 } 68 } 69 getline(cin,s); 70 stringstream sinb(s); 71 while(sinb>>x) 72 v.push_back(x); 73 sort(v.begin(),v.end()); 74 int ans=-1; 75 for(int i=0;i<v.size();i++) 76 { 77 if(i&&v[i]==v[i-1])//去重 78 continue; 79 num[v[i]]--;//从剩余卡牌中去掉一张当前的数的卡牌 80 if(check(v[i]))//dfs爆搜这种情况的状态 81 { 82 ans=v[i]; 83 break; 84 } 85 num[v[i]]++; 86 } 87 printf("%d\n",ans); 88 return 0; 89 }
标签:简单 cst 直接 algorithm 数论 标题 size 思路 col
原文地址:https://www.cnblogs.com/LMCC1108/p/10896950.html