约数倍数选卡片

问题描述

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

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 */
View Code
 2019-02-24
16:29:08

 

posted @ 2019-02-24 16:29  maybeTang  阅读(712)  评论(2编辑  收藏  举报