此题类似于当初上ACM公选课博弈一节的某例题,考察了对于必胜点,必败点等基础知识的掌握。
对于此题:
必败点(P点) :前一个选手(Previous player)将取胜的位置称为必败点。
必胜点(N点) :下一个选手(Next player)将取胜的位置称为必胜点。
对于必败点和必胜点,分别有以下性质:
必败点:无论怎么操作,从必败点下一步都只能进入必胜点。
必胜点:至少存在一种操作,能从必胜点走到必败点。
上述概念看似有点抽象,对于本题,简单理解就是:
当轮到某玩家操作时,无论怎么操作,操作结束后都是对方胜利,此时就是必败点。考虑特殊例子的话,当棋子已经走到了 1 的点,游戏已经结束,这时对于下一个操作的人此时就是必败点。
而如果轮到某玩家操作时,他有多种不同的操作选择,但存在一种操作使得他选择后,对方无论怎么操作都是输,即使对方进入必败点,则该玩家此时处于必胜点。
所以对于本题,我们可以考虑在时间轴上从后往前BFS,因为游戏结束有着明确的规定,即棋子移动到了 1 的点。若棋子已经在 1 的点,则谁先手谁就已经输了(因为在先手操作前棋子已经到达1,则说明是后手达到胜利条件)。
以 1 作为必败点,从后往前搜索,则存在两种情况:
(1).当前状态为必败点。
则上一个状态必为必胜点。(如果某一个状态通过某一个操作能走到必败点,则该点一定是必胜点)
(2).当前状态为必胜点。
此时情况便复杂一点,因为此时有两种可能:
必胜点 --> 必胜点
必败点 --> 必胜点
故我们可以用一个数组保存某个点到达必胜点的次数,若能够达到必胜点的次数等于总的操作数,即无论怎么操作,都只能从当前状态到达必胜点。
则当前状态为必败点。
则便是我下面代码里面的出度数组: deg[]
因为总的操作数是 k1 / k2,故我们可以提前预处理deg数组为总的操作数,一旦能够到达某一个必胜点,则出度减一,当出度为0时,则达到了上述条件。
然后没有更新答案的点就是可以为圈的点。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 1e9 + 7;
const int A = 1e4 + 100;
class P{
public:
int pos,k;
P(int i,int j):pos(i),k(j){}
};
int val_1[A],val_2[A];
int deg_1[A],deg_2[A];
int ans_1[A],ans_2[A]; // 1 必败 2 必胜
queue<P> que;
int main(){
int n;
scanf("%d",&n);
int k1,k2;
scanf("%d",&k1);
for(int i=1 ;i<=k1 ;i++) scanf("%d",&val_1[i]);
scanf("%d",&k2);
for(int i=1 ;i<=k2 ;i++) scanf("%d",&val_2[i]);
ans_1[1] = ans_2[1] = 1;
que.push(P(1,1)),que.push(P(1,2));
for(int i=1 ;i<=n ;i++){
deg_1[i] = k1;
deg_2[i] = k2;
}
deg_1[1] = deg_2[1] = INF;
while(que.size()){
P now = que.front();que.pop();
if(now.k == 1){
for(int i=1 ;i<=k2 ;i++){
int ne = (now.pos - val_2[i] - 1 + n) % n + 1; //避免产生0
if(ans_2[ne]) continue;
if(ans_1[now.pos] == 1){
ans_2[ne] = 2;
que.push(P(ne,2));
}
else{
deg_2[ne]--;
if(deg_2[ne] == 0){
ans_2[ne] = 1;
que.push(P(ne,2));
}
}
}
}
else{
for(int i=1 ;i<=k1 ;i++){
int ne = (now.pos - val_1[i] - 1 + n) % n + 1;
if(ans_1[ne]) continue;
if(ans_2[now.pos] == 1){
ans_1[ne] = 2;
que.push(P(ne,1));
}
else{
deg_1[ne]--;
if(deg_1[ne] == 0){
ans_1[ne] = 1;
que.push(P(ne,1));
}
}
}
}
}
for(int i=2 ;i<=n ;i++){
if(ans_1[i] == 2) printf("Win ");
else if(ans_1[i] == 1) printf("Lose ");
else printf("Loop ");
}
puts("");
for(int i=2 ;i<=n ;i++){
if(ans_2[i] == 2) printf("Win ");
else if(ans_2[i] == 1) printf("Lose ");
else printf("Loop ");
}
return 0;
}