约数倍数选卡片
问题描述
闲暇时,福尔摩斯和华生玩一个游戏:
在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
1,2,3, 6,12,18,24 ....
当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。
在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
1,2,3, 6,12,18,24 ....
当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。
输入格式
输入数据为2行。第一行是若干空格分开的整数(每个整数介于1~100间),表示当前剩余的所有卡片。
第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。
第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。
输出格式
程序则输出必胜的招法!!
样例输入
2 3 6
3 6
3 6
样例输出
3
样例输入
1 2 2 3 3 4 5
3 4 5
3 4 5
样例输出
4
Algorithm
最开始这样想,我们先选一个数,然后列举出所有可能的情况。最后这是一棵树......
所以就准备广搜,搜了很久之后发现,广搜好像不行,因为每个点广搜只经过一次,但是我们的数字如果这样不会输可能还会拿来下一次进行下一次的方案中选取。
我没能想到很好的方法来进行标记。
而深搜就不一样了,每当这个数用过之后我们可以将它还原,以便于下一次接着用。
值得注意一点的地方是去重,和将数字排序一下。以及我们标记数字时,应该标记它还有几个可以用,比如这里有两个2和两个3,那么应标记它出现了两次,而不是出现过。
去重的加速可能不太可观,但是排序便于我们选取。
我还看到了从大到小进行搜索的方法,理由是,大数的约数倍数更少,但是有个问题是,我们是要输出最小的必赢的值......
这个程序递归的地方有点不好理解。先给自己记一笔......
这是我参考了诸多博客才实现的,有必要仔细研究一下。
AC
1 /* 2 * 万能的搜索... 3 */ 4 #include<iostream> 5 #include<string> 6 #include<queue> 7 #include<vector> 8 #include<sstream> 9 #include<algorithm> 10 #include<cstring> 11 12 using namespace std; 13 14 const int MAX = 109; 15 vector<int> v[MAX]; 16 vector<int> c; 17 // 这里的状态标记不能这样标记, 应标记出现多少次 18 int book[MAX]; 19 20 bool DFS(int n) 21 { // 寻找 n 的约数倍数, 如果没有, 就会直接返回true(赢了) 22 // 1; 23 for(int i=v[n].size()-1;i>=0;i--){ 24 int now = v[n].at(i); 25 if(book[now]){ // 判断当前数有没有用完 26 book[now]--; 27 bool win = DFS(now); 28 book[now]++; 29 if(win) return false; 30 } 31 } 32 33 return true; 34 } 35 36 int main() 37 { 38 int x; 39 string s; 40 41 getline(cin, s); 42 stringstream ss(s); 43 // 统计每个数出现的次数 44 while(ss>>x) book[x]++; 45 getline(cin, s); 46 stringstream str(s); 47 // 我们可以选择的数 48 while(str>>x) c.push_back(x); 49 // 排序, 方便从小到大搜, 搜到的第一个就是最小的值 50 sort(c.begin(), c.end()); 51 // 预处理可选数的约数和倍数 52 for(int i=1;i<MAX;i++) 53 if(book[i]) 54 for(int j=1;j<MAX;j++) 55 if(book[j] && (i%j == 0 || j%i == 0)) 56 v[i].push_back(j); 57 // 搜索 58 for(int i=0;i<c.size();i++){ 59 int now = c.at(i); 60 if(book[now]){ 61 book[now]--; 62 if(DFS(now)){ 63 cout<<now<<'\n'; 64 return 0; 65 } 66 // 用完之后一定要还, 不然下一次没法搜 67 book[now]++; 68 } 69 } 70 cout<<-1<<'\n'; 71 72 return 0; 73 } 74 75 76 /* 77 void Init(int *a, int *b, int la, int lb) 78 { // 先把 a 约数倍数处理一下 79 // 想办法去重... 80 sort(a, a+la); 81 // int *end = unique(a, a+la); 82 // la = end - a; 83 // a[i] 的约数和倍数是 a[j] 84 for(int i=0;i<la;i++) 85 for(int j=0;j<la;j++){ 86 if(i == j) 87 continue; 88 else if(i > 0 && a[i] == a[i-1]) 89 continue; 90 else if(a[i]%a[j] == 0 || a[j]%a[i] == 0) 91 v[a[i]].push_back(a[j]); 92 } 93 94 return; 95 } 96 97 int DFS(int start, int step) 98 { cout<<"DFS:"<<step<<endl; 99 // 发现可以赢就返回了..., 万一输了判断不了啊... 100 if(book[start] == 0){ 101 // 如果一共进行了 奇数 次行动, 那么获胜 102 return (step&1 == 1)?1:0; 103 } 104 book[start]--; 105 int t = v[start].size(); 106 for(int i=0;i<t;i++){ 107 int num = v[start].at(i); 108 if(book[num] == 0) 109 continue; 110 // book[num]--; 111 DFS(num, step++); 112 book[num]++; 113 } 114 } 115 116 int BFS(int start) 117 { // 突然发现, 广搜好像不好做, 因为标记这关不太好处理 118 // 可能会多次用到, 但是我们无法很好地还原前面一个点 119 queue<int> q; 120 q.push(start); 121 while(!q.empty()) 122 { 123 int t = q.front(); 124 book[t]--; 125 q.pop(); 126 int len = v[t].size(); 127 for(int i=0;i<len;i++){ 128 if(book[v[t].at(i)] == 0) 129 continue; // 这个数已经用完了 130 if(len > 1 && i != 0 && v[t].at(i) == v[t].at(i-1)) 131 continue; // 这个数已经进过一次队了 132 int x = v[t].at(i); 133 q.push(x); 134 book[x]--; 135 break; 136 137 } 138 139 140 break; 141 } 142 143 144 return -1; 145 } 146 147 void fun(int *a, int *b, int la, int lb) 148 { 149 sort(b, b+lb); 150 memset(book, 0, sizeof(book)); 151 for(int i=0;i<la;i++) 152 book[a[i]]++; 153 154 int ret = 999; 155 Init(a, b, la, lb); 156 int c = 999; 157 for(int i=lb-1;i>=0;i--){ 158 memset(book, 0, sizeof(book)); 159 c = DFS(b[i], 1); 160 cout<<b[i]<<":->"<<c<<endl; 161 } 162 cout<<(c == 999)?-1:ret<<'\n'; 163 return; 164 } 165 166 int main() 167 { 168 string s1, s2; 169 int i = 0, j = 0; 170 171 getline(cin, s1); 172 stringstream ss1(s1); 173 while(ss1>>a[i++]); 174 175 getline(cin, s2); 176 stringstream ss2(s2); 177 while(ss2>>b[j++]); 178 179 fun(a, b, i-1, j-1); 180 181 return 0; 182 } 183 184 */
2019-02-24
16:29:08