一个环上N个位置。两个人轮流玩游戏,每次可以从各自的集合中选择任意一个数作为自己的步长,走到1号点就输了。问把2到n号位置作为出发点,两人中各自先手共2*(N-1)种情况中,最终先手会获胜、失败还是平局。
这题,直接从每一种情况向前推肯定行不通,因为情况太多了。
这样来看,从必败态1号点逆推似乎比较简单。
接着按照博弈论中的理论从1号点向前逆推。可达到必败态的点,一定是必胜点,而所有后续状态全为对方必胜态的点则为必败点。每次从集合中选数,将新状态加入队列即可。
用1和2表示上一步是谁走的。
怎么判断是否全部的后续状态都是对方必胜态?可以开两个数组,大小与圆环长度相同,最开始的值为自己集合内数的个数,每推到一个就将值-1,值为0代表所有状态都被推完。
#include <cstdio>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
const int maxn=7005;
int dp[maxn],dp2[maxn],a[maxn],b[maxn],r[maxn],r2[maxn];
struct node {
int pos,pre;
node(int pos,int pre): pos(pos),pre(pre) {}
};
int main() {
int n,k1,k2,i,h=0,t=0,cnt,j;
scanf("%d%d",&n,&k1);
memset(dp,0,sizeof(dp));
memset(dp2,0,sizeof(dp2));
for (i=1;i<=k1;i++) {
scanf("%d",&a[i]);
}
dp[0]=2;
scanf("%d",&k2);
int h2=0,t2=0;
for (i=1;i<=k2;i++) {
scanf("%d",&b[i]);
}
for (i=1;i<=n;i++) {
r[i]=k1;r2[i]=k2;
}
dp2[0]=2;
cnt=1;
queue<node> q;
q.push(node(0,1));
q.push(node(0,2));
while (!q.empty()) {
int now=q.front().pos,p=q.front().pre;
q.pop();
if (p==1) {
for (i=1;i<=k2;i++) {
int pos=now-b[i];
if (pos<0) pos+=n;
if (!dp2[pos]) {
if (dp[now]==2) {
dp2[pos]=1;
q.push(node(pos,2));
} else {
r2[pos]--;
if (r2[pos]==0) {
dp2[pos]=2;
q.push(node(pos,2));
}
}
}
}
} else {
for (i=1;i<=k1;i++) {
int pos=now-a[i];
if (pos<0) pos+=n;
if (!dp[pos]) {
if (dp2[now]==2) {
dp[pos]=1;
q.push(node(pos,1));
} else {
r[pos]--;
if (r[pos]==0) {
dp[pos]=2;
q.push(node(pos,1));
}
}
}
}
}
}
for (i=1;i<=n-1;i++) {
if (dp[i]==0) printf("Loop "); else
if (dp[i]==1) printf("Win "); else printf("Lose ");
}
printf("\n");
for (i=1;i<=n-1;i++) {
if (dp2[i]==0) printf("Loop "); else
if (dp2[i]==1) printf("Win "); else printf("Lose ");
}
return 0;
}