又是好几天之前的题目了,这几天一直沉迷于王者农药,今天总算把这道题写了。
考试的时候:看完题面,博弈论????emmm好像从出度为0的点倒着递推过来就行了?(从来没写过博弈论),可是平局怎么弄,,,行吧不写了(没时间了)。
考完之后跟同学说这道题,他告诉我这不是个博弈问题。。。。又去认认真真读了一遍样例,确实不是。
官方拖了好几天才发题解。
先看赢的情况,这不是一个博弈问题,后手要帮着先手赢的,所以其实就是找一条路径,从s开始,经过奇数条边,最终到达的点出度为0。那么我们就可以类似于拆点的思想,记录到达一个点的时候经过了奇数条边或是偶数条边是否可能,从s开始dfs转移即可判断能否获胜,沿途记录一下当前路径上的点并回溯即可。
那平局情况又是什么呢,我们发现边数一共才4e5(拆点之后的),一条不重复走的路经是不可能走出1e6的,只有可能是环,那么就是判断能不能到达一个环,dfs的时候打一下标记并回溯即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=1e5+10; 4 int n,m,s; 5 int tot,fi[inf],nxt[inf<<1],to[inf<<1]; 6 int f[inf][2]; 7 bool is[inf]; 8 int flag; 9 void link(int x,int y){ 10 to[++tot]=y;nxt[tot]=fi[x];fi[x]=tot; 11 } 12 int sta[inf<<1],top; 13 int vis[inf][2]; 14 void dfs(int x,int y){ 15 vis[x][y]=1; 16 f[x][y]=1; 17 sta[++top]=x; 18 if(is[x] && y){ 19 printf("Win\n"); 20 for(int i=1;i<=top;i++)printf("%d ",sta[i]); 21 puts(""); 22 exit(0); 23 } 24 for(int i=fi[x];i;i=nxt[i]){ 25 if(!flag && (f[to[i]][0] || f[to[i]][1]))flag=1; 26 if(!vis[to[i]][y^1])dfs(to[i],y^1); 27 } 28 f[x][y]=0; 29 top--; 30 } 31 int main() 32 { 33 scanf("%d%d",&n,&m); 34 for(int i=1;i<=n;i++){ 35 int x; 36 scanf("%d",&x); 37 for(int j=1;j<=x;j++){ 38 int y; 39 scanf("%d",&y); 40 link(i,y); 41 } 42 if(!x)is[i]=1; 43 } 44 scanf("%d",&s); 45 dfs(s,0); 46 if(flag)printf("Draw\n"); 47 else printf("Lose\n"); 48 return 0; 49 }
那个记录路径点数的数组要开2倍大小的,因为拆点了,每个点可以过两次,不然有可能由于数组越界导致奇怪的事情。