题目
给定一个DAG,起点1出有一个棋子,先手后手依次将他向出边移动,最先无法移动的人输,求先手是否必胜。
//因为只要能转移到任意的对手的必败态,便为必胜态,反之为必败态,故只会有必胜/必败态存在。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define maxn 10010
#define maxm 10010
using namespace std;
struct Edge{
int u;
int v;
Edge *next;
} edge[maxm];
bool win[maxn];
int n, m;
int out_edge[maxn];
int topo_queue[maxn], q_end = 0, q_begin = 0;
Edge *V[maxn];
int main() {
cin >> n >> m;
memset(out_edge, 0, sizeof(out_edge));
for (int i = 1; i <= m ; i++) {
cin >> edge[i].u >> edge[i].v;
edge[i].next = V[edge[i].v];
V[edge[i].v] = &edge[i];
out_edge[edge[i].u]++;
}//邻接表储存,同时记录出度。
for(int i = 1; i <= n; i++)
if(!out_edge[i]) topo_queue[q_end++] = i;//将所有出度为0的点加入处理队列中
for(int i = 0; i < q_end; i++)
win[i] = false; //所有点初始为先手无法必胜,因先手到达无法走的点即败
while(q_begin < q_end) {
int i = topo_queue[q_begin++];
win[i] = !win[i]; //win[i]表示以i点位起点,先手是否必胜,这里将之前表示的能否到达先手必败态的节点 反转为先手是否必胜
for(Edge *e = V[i]; e; e = e->next) {
if(--out_edge[e->u] == 0) {
win[e->u] = win[i] || win[e->u];//只要以此节点为起点走能转移到任意一个先手必胜态的起点,此节点先手必败。
// 这里记录的是能否到达一个先手必胜态的起点。当此节点出队时再反转结果
topo_queue[q_end++] = e->u;
}
} //toposort删边过程
} //toposort过程
if(win[1]) cout << "win";
else cout << "lose";
return 0;
}
ps:最近代码风格变化的略快略多啊。。。