CSP - S 初赛 取石子

#include <bits/stdc++.h>
const int maxn = 64;
int n, m;
int a[maxn], b[maxn];
unsigned long long status, trans;
bool win;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i)
scanf("%d%d", &a[i], &b[i]);
for(int i = 0; i < n; ++i)
for(int j = i + 1; j < n; ++j)
if (a[i] > a[j]){
swap(a[i], a[j]);
swap(b[i], b[j]);
}
status = ~0ull ^ 1;
trans = 0;
for(int i = 1, j = 0; i <= m; ++i){
while (j < n && a[j] == i){
trans |= 1ull << (b[j] - 1);
++j;
}
win = ~status & trans;
status = (status << 1) ^ win;
}
puts(win ? "Win" : "Loss");
return 0;
}
答案就是这样 , 题目的其他选项我就不放了 , 想找的可以去网上搜搜看 , 我这里用答案的代码来讨论。
主要面临的问题是trans 和 status 这两个状压变量的具体含义 。
我是这样认为的 ,
这里我用变量 j 代表此时剩余的石子个数 。
trans的含义较为简单因为我们知道b[i] <= 64所以我们可以把状压中每一位的1 , 0代表为对于剩余 j 个石子时否有获胜方案。
我们判断 , 如果此时剩下b[i] == j个石子时 , 由先手取就拿走b[i]个石子 , 必胜。
status的含义在认真观察之后可以发现status在循环最后增加当剩余第i个石子时是否有获胜方案的状态 , 相当于倒着存。
由于我们倒着存 , 所以剩余 b[i] 个石子时 , 他会对应 剩余 j - b[i] 个石子的情况。
我们能这么存的原因是b[i] <= 64所以n个石子最多只能从j - 64转移过来,我们就可以动态的更新这个状态。
我们将它取反的含义是因为当前面为j - b[i]的剩余石子时 ,如果是后者胜 , 那么接下来就轮到先手取就取到b[i] , 就能得到一个必胜方案。
所以我们取反之后 , 让 ~(j - b[i])的状态 和 b[i]的状态相& , 就可以得到当剩余 j 个石子是否有必胜方案。
最后win用于判断更新到m个石子时 , 我们是否能胜利。

浙公网安备 33010602011771号