首页 > 技术文章 > 2020-2021年度第二届全国大学生算法设计与编程挑战赛(冬季赛)——热身赛 D题

greenpepper 2022-03-17 22:02 原文

2020-2021年度第二届全国大学生算法设计与编程挑战赛(冬季赛)——热身赛 D题

链接:http://vj.saikr.com/contest/10/problem/D

题意:

给出一个图,分山顶山谷,只有山顶和山谷之间连边,显然得图为二分图。

从左侧点出发,轮流走,直到不能走,停在顶则Cong胜,反之则Ni胜,自然可想到博弈论方向。

分析:

我们对其跑一个最大匹配,其增广路径为匹配边,非匹配边交替。

  • 若一条连续增广路径如图,左侧点数大于右侧点数,则无论如何,从左侧出发,最后必然会落在左侧。

  • 若一条连续最长增广路径如图,左侧点数小于等于右侧点数,则无论如何,从左侧出发,最后必然落在右侧。

我们建立超级源点s和超级汇点t,对该二分图跑一个最大流。

对于偶数个边,必然在增广路径两头有一个左侧的点没有流量,所以我们从超级源点按照容量余下1的边遍历(从右向左为反边的流量容量),所有遍历到的左侧点为败点。

对于奇数个边,则增广路径中的左侧点必然都有流量,所以我们从超级源点遍历时遍历不到,故未被遍历到的点都为胜点。

结论

我们对原二分图用超级源汇跑一个最大流,在从源点bfs所有流量容量为1的边。所有被标记的左点都为Cong赢,没有被标记的左点都为Ni赢。

/***********************************
dinic最大流思路:
    不断图分层,保证dfs过程中,不会出现环等现象
    在一次分层中,用dfs一次性搜寻多条增广路。
    就是向下搜,搜到t为止,然后返回流量。
    当前节点最大流量为的下属分支所有流量的返回最大流量。
    下属分支返回的流量为min(通往该分支边的最大流量,分支的下属总流量)
    当dfs一次后,一定会有某些边被榨干,改变分层图。
    再次分层图,重复上述过程。
    分层图用bfs实现。如果bfs不到终点,则结束算法。
************************************/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
typedef long long ll;
typedef long long ii;
const ll maxn = 1e5 + 50;
const ll maxm = 1e5 + 50;
const ll INF = ll(1) << 50;
struct e {
	ll next, to, flow;
} edge[maxm * 2];
int head[maxn * 2], tot = 0;
void add(ll u, ll v, ll flow) {
	edge[tot].to = v; edge[tot].next = head[u];
	edge[tot].flow = flow; head[u] = tot++;
	edge[tot].to = u; edge[tot].next = head[v];
	edge[tot].flow = 0; head[v] = tot++;
}
ll n, m, s, t;

ll dis[maxn * 2];
queue<ll> q;
ll cur[maxn * 2];

ll dfs(ll u, ll lim) {
	if (u == t)return lim;
	ll flow = 0;
	for (ll i = cur[u]; ~i; i = edge[i].next) {
		cur[u] = i;
		ll v = edge[i].to;
		if (dis[v] != dis[u] + 1 || edge[i].flow == 0) {
			continue;
		}
		ll f = dfs(v, min(edge[i].flow, lim));
		flow += f; lim -= f;
		edge[i].flow -= f; edge[i ^ 1].flow += f;
		if (lim == 0)break;
	}
	return flow;
}

ll bfs() {
	while (!q.empty())q.pop();
	for (ll i = 0; i <= n * 2 + 1 ; i++) {
		dis[i] = INF;
		cur[i] = head[i];
	}
	dis[s] = 0;
	q.push(s);
	while (!q.empty()) {
		ll u = q.front(); q.pop();
		for (ll i = head[u]; ~i; i = edge[i].next) {
			ll v = edge[i].to;
			if (edge[i].flow == 0 || dis[v] != INF)continue;
			dis[v] = dis[u] + 1;
			q.push(v);
		}
	}
	if (dis[t] == INF)return 0;
	else return 1;
}
void dfs4(int u, int p) {

}
void init() {
	for (ll i = 0; i <= n * 2 + 50; i++) {
		head[i] = -1;
	}
}
int check[maxn * 2];
void bfs2() {
	queue<int> q;
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		for (int i = head[u]; ~i; i = edge[i].next) {
			int v = edge[i].to;
			if (edge[i].flow == 1 && check[v] != 1 ) {
				q.push(v);
				check[v] = 1;
			}
		}
	}
}
int main() {
	scanf("%lld%lld", &n, &m);
	init();
	s = 0; t = 2 * n + 1;
	ll a, b;
	for (int i = 1; i <= n; i++) {
		add(s, i, 1);
		add(i + n, t, 1);
	}
	for (int i = 0; i < m; i++) {
		scanf("%lld%lld", &a, &b);
		add(a, b + n, 1);
		//add(b + n, a, 1);
	}
	ll maxflow = 0;
	while (bfs()) {
		maxflow += dfs(s, INF);
	}
	bfs2();
	for (int i = 1; i <= n; i++) {
		if (check[i] == 0) {
			puts("Ni");
		} else {
			puts("Cong");
		}
	}
	return 0;
}

推荐阅读