【题意】:两个人在玩数字游戏,规则如下:有2~20这19个数,每个回合每个人轮流取走一个数k,k取走后k的所有倍数都不可以再取,并且任意两个不可取的数的和也不可以取。给出当前的合法局面,问先手是否必胜,若必胜输出最优操作。

【题解】:考虑到一共只有19个数字,可以用状态压缩。然后在博弈树上记忆化搜索即可,找到第一个必胜局面就可以跳出了。

【代码】:
 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 #include "algorithm"
 5 #include "vector"
 6 #include "queue"
 7 #include "cmath"
 8 #include "string"
 9 #include "cctype"
10 #include "map"
11 #include "iomanip"
12 using namespace std;
13 #define pb push_back
14 #define lc(x) (x << 1)
15 #define rc(x) (x << 1 | 1)
16 #define lowbit(x) (x & (-x))
17 #define ll long long
18 #define MAX 524287
19 int n, x[20];
20 int dp[MAX+10];
21 vector<int> vec;
22 
23 void getmask(int &tmp, int &forbid, int x) {
24     for(int i = x; i <= 20; i += x) {
25         if(tmp & (1<<(i-2))) {
26             tmp &= ~(1<<(i-2));
27             forbid |= (1<<(i-2));
28         }
29     }
30     for(int i = 2; i <= 20; i++) {
31         if(forbid & (1<<(i-2)))
32             for(int j = i + 1; j <= 20 - i; j++) {
33                 if((forbid & (1<<(j-2))) && (tmp & (1<<(i+j-2)))) {
34                     tmp &= ~(1<<(i+j-2)), forbid |= (1<<(i-2));
35                 }
36             }
37     }
38 }
39 
40 bool g(int mask, int forbid) {
41     if(dp[mask] != -1) return dp[mask];
42     for(int i = 2; i <= 20; i++) {
43         if(mask & (1<<(i-2))) {
44             int nmask = mask, nforbid = forbid;
45             getmask(nmask, nforbid, i);
46             if(!g(nmask, nforbid)) return dp[mask] = 1;
47         }
48     }
49     return dp[mask] = 0;
50 }
51 
52 void init() {
53     memset(dp, -1, sizeof(dp));
54     dp[0] = 0;
55 }
56 
57 int main() {
58     int T, Case = 1;
59     scanf("%d", &T);
60     init();
61     while(T--) {
62         vec.clear();
63         scanf("%d", &n);
64         int mask = 0;
65         for(int i = 0; i < n; i++) {
66             scanf("%d", &x[i]);
67             mask |= (1<<(x[i]-2));
68         }
69         int forbid = MAX & ~mask;
70         for(int i = 0; i < n; i++) {
71             int nmask = mask, nforbid = forbid;
72             getmask(nmask, nforbid, x[i]);
73             if(!g(nmask, nforbid)) vec.pb(x[i]);
74         }
75         printf("Scenario #%d:\n", Case++);
76         if(vec.size()) {
77             sort(vec.begin(), vec.end());
78             printf("The winning moves are:");
79             for(int i = 0; i < vec.size(); i++) printf(" %d", vec[i]);
80             printf(".\n\n");
81         } else printf("There is no winning move.\n\n");
82     }
83     return 0;
84 }