P6560 [SBCOI2020] 时光的流逝(DAG图上博弈模板)
P6560 [SBCOI2020] 时光的流逝(DAG图上博弈模板)
题意:
给出一个有向图(可能有环),每轮游戏有一个起点和终点,A和B一起玩游戏。A先移动,然后他们交替移动,当谁把棋子移动至终点,谁就胜利了。同样,若是有人无法移动了,就会被判失败。若A必胜,输出1,若B必胜,输出-1,若两人均无必胜策略,输出0。
思路:
很明显是一个博弈论。对于这种DAG上的博弈游戏,我们要从终点来考虑问题。分析题目,因为当棋子到达终点或者到达出度为0的点,此时执棋者失败。所以,终点和出度为0的点是必败态。这里把出度为0的点也归到下文终点的定义中。
对于图上的博弈论,有一个很基础的知识点:若一个点可以到达必败态,那么这个点是必胜态。若一个点无法达到必败态,只能到达必胜态,那么这个点是必败态。
因为我们一开始只知道终点的信息,所以我们应该建一个反图,然后从终点开始拓扑排序,根据上面的知识点进行模拟。
实现:
在拓扑排序的时候要通过出度(反图的入度)来模拟判断一个点是否为必胜态。本题还有一个问题就是,什么叫均无必胜策略,那也就是遇到了一个环,比如simple2中的数据。我们在拓扑排序前对他们做标记,若拓扑排序做完之后,终点还没有进过队列,那就是环也就是输出0。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, n) for (int i = a; i < n; i++)
#define all(x) x.begin(), x.end()
#define pb push_back
#define debug(x) cout << x << endl
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
string yes = "Yes\n";
string no = "No\n";
const int N = 100005;
int n, m, Q;
int d[N], f[N], back_d[N]; //入度会被更改,所以要备份一下。
vector<int> g[N];
int main()
{
scanf("%d%d%d", &n, &m, &Q);
while (m--)
{
int u, v;
scanf("%d%d", &u, &v);
g[v].pb(u);
d[u]++;
}
while (Q--)
{
int st, ed;
scanf("%d%d", &st, &ed);
queue<int> q;
memcpy(back_d, d, sizeof back_d);
for (int i = 1; i <= n; i++) //入度
{
if (i == ed || !back_d[i])
f[i] = -1, q.push(i); // f --> Lose:-1 Win:1
else
f[i] = 0; //不可达就是0
}
while (q.size())
{
int u = q.front();
q.pop();
for (int v : g[u])
{
if (f[v] != 0)
continue;
back_d[v]--;
if (f[u] == -1) //如果一个点能走到一个必败点,那就是必胜态
{
f[v] = 1;
q.push(v);
}
else if (!back_d[v])
{
f[v] = -1;
q.push(v);
}
}
}
cout << f[st] << '\n';
}
}