题意:
给你n(<=100)个点m(<=n*(n-1))条边的有向简单图,Alice和Bob(两个人都足够聪明)在这个图上玩游戏,两个人轮流沿着有向图走,一次只能走一条边,Bob先走,如果Alice和Bob走到同一个点,或者Bob无法走了,则Bob输,否则Bob赢(Alice永远追不上Bob或者Alice无路可走)。如果Bob能赢输出Yes,否则输出No。
思路:
考虑记忆化搜索。但是由于图并不是DAG,我们只能倒着从已知的状态往前推,从而找出所有的Bob的必败态。
dp[i][j][k]表示Bob在i点,Alice在j点,轮到k(k=0表示该Bob走了,k=1表示该Alice走了)走了时,Bob是否能赢(不能则值为0)。
显然可以找出两种Bob的必败态:
1、该Bob走了,并且无路可走
2、无论该谁走了,Bob和Alice在同一个点
将两种必败态的所有点加入队列,假设当前Bob在x点,Alice在y点,依次判断:
1、如果下一步该Bob走了并且是(Bob的)必败态,那么Alice直接从y的前一个点ny走到y。将得到的新的必败态(x,ny,1)加入队列。
2、如果下一步该Alice走了并且是(Bob的)必败态,那么枚举x的前驱节点nx,如果nx的所有后继状态都是必败态,那么得到新的必败态(nx,y,0)加入队列。
由于nx的所有后继状态都是必败态我们才去判断它,所以可以先记录好每个节点的出度,然后倒着建图,用一个cnt[i][j]表示i的(倒着建图的)后继节点的必败态数量,这样更方便判断。
代码:
#include
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define dep(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int maxn=4e2+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
int n,m,k;
int a[maxn],c[maxn];
int ans,tmp;
int flag;
char s[maxn];
bool ok[maxn];
vectorvc[maxn],vv;
int dp[maxn][maxn][2];
int cnt[maxn][maxn];
int du[maxn];
template
inline void read(T &X)
{
X=0;int w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
if(w) X=-X;
}
struct node{
int x,y;
int z;
node(){}
node(int xx,int yy,int zz){
x=xx;y=yy;z=zz;
}
};
queueq,p;
int main()
{
int T,cas=1;
read(T);
while(T--)
{
read(n);read(m);
q=p;
ans=0; flag=0;
int sb,sa;
rep(i,1,n) {vc[i].clear();du[i]=0;}
rep(i,0,n)
rep(j,0,n)
rep(k,0,1){
dp[i][j][k]=-1;
cnt[i][j]=0;
}
rep(i,1,n)
rep(k,0,1){
dp[i][i][k]=0;
q.push(node{i,i,k});
}
vv.clear();
rep(i,1,m){
int x,y;
read(x);read(y);
vc[y].push_back(x);
du[x]++;
}
rep(i,1,n) if(!du[i]){
vv.push_back(i);
}
for(int k=0;k