Codeforces Round #406 (Div. 2) C 有向图博弈,dp
题意:n个星球围成一个圏,第一个星球是黑洞,有一个怪物在第2~n个星球上的任意一个。两个人分别有两个数集S1、S2,两人轮流从数集中选一个数 m,让怪物走 m 步。走到第一个星球则败,问怪物在第2~n个星球上时,先手胜负。
tags: 跑bfs,倒着来推。 (1)初始时,假设现在走到了第一个星球,为必败态,则往前推一步可以直接确定为必胜态。 (2)然后可以继续递推下去。如为必败,则上一步为必胜;如为必胜,则上一步可能为胜的概率就 -1,这里概率可以用个数组存好,初始为k[i],当上一步概率减至0时即为必败。 (3)最后没有遍历到的状态就是无限循环。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a;i<=b;i++) #define per(i,b,a) for (int i=b;i>=a;i--) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define PII pair<int ,int > #define MP make_pair typedef long long ll; const int N = 7005; int k[3], a[3][N], dp[3][N], wn[3][N]; // dp[j][i]表示第j个人先手且怪物在第i+1点的胜负,wn[j][i]表示此状态可能胜的数量 PII q[N<<2]; int n, top; int main() { scanf("%d", &n); scanf("%d", &k[0]); rep(i,1,k[0]) scanf("%d", &a[0][i]); scanf("%d", &k[1]); rep(i,1,k[1]) scanf("%d", &a[1][i]); rep(i,0,n-1) wn[0][i]=k[0], wn[1][i]=k[1]; mes(dp, -1); dp[0][0]=dp[1][0]=0; //怪物在第一个点为必败态 q[0]=MP(0,0); q[1]=MP(1,0); top=1; rep(i,0,top) { int u=q[i].first, v=q[i].second; if(dp[u][v]==0) { //必败状态 rep(j,1,k[u^1]) { int uu=u^1, vv=(v+n-a[uu][j])%n; if(dp[uu][vv]==-1) { //把能转移到必败状态的状态直接标为必胜状态,并加入队列 dp[uu][vv]=1; q[++top]=MP(uu,vv); } } } else { //必胜状态 rep(j,1,k[u^1]) { int uu=u^1, vv=(v+n-a[uu][j])%n; --wn[uu][vv]; //把能转移到必胜状态的状态的出度 -1,当它的出度为0且它没有遍历过时,就可以把它标为必败状态,并加入队列 if(dp[uu][vv]==-1 && wn[uu][vv]==0) { dp[uu][vv]=0; q[++top]=MP(uu,vv); } } } } rep(i,0,1) { rep(j,1,n-1) { if(dp[i][j]==-1) printf("Loop "); //没有遍历到的点即为无限循环 else if(dp[i][j]==0) printf("Lose "); else printf("Win "); } puts(""); } return 0; }