一般的博弈论都有必败态和必胜态,都是遇到某个状态发现是已经被决定的。
对于这题,先观察特殊状态,因为题目是个有向图,所以所有出度为0的点都是有特定状态的,因为他没有路可以走
我们设状态为f[i][j],表示在i这个点,是j行动,我们假定初始态是-1,表示平局,0表示A赢,1表示B赢,b[i]表示某人真的希望赢的那方
在出度为0的状态决定以后,只需要让他遍历能到当前点的点,暂时记为X,也就是存反向边,因为操作的顺序一定,所以前一个操作的就是i-1(1是前一个是6)
如果对于前一个操作人在X的期望和这个点相同,那么就可以更改状态,以下都默认是前一个人在X的期望状态
如果全部能到达X点的期望答案都与X的期望答案相反,那么X就是必败的。
这样只需要用一个队列就能扩展更新所有状态。
对于没有更新到状态的,那么就是表示平局情况
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=2e5+10; int f[N][10]; int n,m; int b[N],du[N][10]; string s,t; vector<int> num[N]; int real1[N]; int pre(int x){ if(x==1) return 6; else return x-1; } void solve(){ queue<pll> q; int i,j; for(i=1;i<=n;i++){ if(du[i][1]==0){ for(j=1;j<=6;j++){ if(s[j]=='A'){ f[i][j]=1; q.push({i,j}); } else{ f[i][j]=0; q.push({i,j}); } } } } while(q.size()){ auto x=q.front(); q.pop(); for(auto e:num[x.first]){ if(f[e][pre(x.second)]==-1){ if(f[x.first][x.second]==b[pre(x.second)]){ f[e][pre(x.second)]=b[pre(x.second)]; q.push({e,pre(x.second)}); } else{ du[e][pre(x.second)]--; if(du[e][pre(x.second)]==0){ f[e][pre(x.second)]=b[pre(x.second)]^1; q.push({e,pre(x.second)}); } } } } } } int main(){ ios::sync_with_stdio(false); int T; cin>>T; while(T--){ cin>>n>>m; memset(f,-1,sizeof f); memset(du,0,sizeof du); int i,j; for(i=0;i<=n;i++) num[i].clear(); for(i=1;i<=m;i++){ int a,b; cin>>a>>b; num[b].push_back(a); for(j=1;j<=6;j++){ du[a][j]++; } } cin>>s>>t; s=" "+s; t=" "+t; for(i=1;i<=6;i++){ real1[i]=s[i]-'A'; b[i]=(t[i]-'0')^real1[i]; } solve(); for(i=1;i<=n;i++){ if(f[i][1]==-1){ cout<<'D'; } else if(f[i][1]==1){ cout<<'B'; } else{ cout<<'A'; } } cout<<endl; } }