C/C++教程

Acwing411 国王的任务 题解

本文主要是介绍Acwing411 国王的任务 题解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

题目描述

曾经有一个国王,他有 \(N\) 个儿子。
王国中有着 \(N\) 个漂亮的姑娘,每个王子也都有自己喜欢的对象。
每个王子喜欢的对象可能不止一个。
因为王子们都到了结婚的年纪,所以国王想让王子们娶了这 \(N\) 个姑娘,当然每个姑娘只能嫁给一名王子。
国王请巫师为他做一个统计,他想看看儿子们都有哪些喜欢的姑娘。
就这样,巫师制作了一个清单,上面具体列出了每一个王子喜欢哪些姑娘,并给出了一套初步的配对方案。
国王看了看巫师给他列出的清单,说道:“你总结的不错,但是我并不完全满意。我希望你列出每个王子可以婚配的女子清单,可以满足每个王子对应的清单上都是他喜欢的姑娘,并且任何一个王子从自己的清单上任意选择一名姑娘作为自己的结婚对象之后,剩下的王子仍然能够从自己的清单中选择的到自己喜欢的对象,使得所有的王子都能完成和自己喜欢的姑娘配对。”
请你帮助巫师列出国王满意的清单。

输入格式

第一行包含整数 \(N\)。
接下来 \(N\) 行,每行包含多个整数,描述了一个王子喜欢的姑娘的清单,每行的第一个整数 \(K\),表示王子喜欢的姑娘的数量,接下来的 \(K\) 个整数,为这 \(K\) 个姑娘的编号。
最后一行,包含 \(N\) 个整数,表示初步的配对方案,第 \(i\) 个整数表示与第 \(i\) 个王子进行配对的姑娘编号。

输出格式

输出共 \(N\) 行。
每行包含多个整数,描述一个王子可以婚配的女子清单,第一个整数 \(L\),表示王子可以婚配的姑娘的数量,接下来 \(L\) 个整数,为这 \(L\) 个姑娘的编号(请按升序排列)。

数据范围

\(1≤N≤2000\),所有 \(K\) 加起来的和不超过 \(200000\)。

输入样例:

4
2 1 2
2 1 2
2 2 3
2 3 4
1 2 3 4

输出样例:

2 1 2
2 1 2
1 3
1 4

题解

此题要求求出所有的边,满足匹配这条边后,二分图仍能构成完备匹配。
容易想到一个策略:显然给定的所有匹配边一定是答案。我们再枚举每条非匹配边\((a_1,b_1)\),其中\(match[b_1]=a_2\),\(match[b_2]=a_1\)。修改\(match[b_1]=a_1\),\(match[b_2]=0\),用匈牙利算法判断\(a_2\)能否成功匹配。若成功,则边\((a_1,b_1)\)为所求的边。
然而这种方法的时间复杂度为\(O(NM)\),会超时。需要更快速的解法。
注意到原图一定为完备匹配,这也就意味着,若\(a_2\)能成功匹配,则dfs搜索树中的最后一个节点一定是\(b_2\)。而\(b_2\)原来是与\(a_1\)匹配。于是我们发现:\(a_1→b_1→a_2→...→b_2→a_1\)构成了一个环!若这个环上右部有\(b_3,b_4,...,b_k\),且它们与\(a_1\)有边,那么这些b点就有着与\(b_1\)相同的性质,\((a_1,b_{1...k})\)同样是答案。这些边就不再需要通过匈牙利算法再次判断了。
不难发现,环的指向中所有\(a→b\)是非匹配边,是王子喜欢但没有娶的女子;\(b→a\)是匹配边,是巫师列出的清单上的婚配方案。我们依上建立有向图,并在此图中求强联通分量。那么对于\((a,b)\)满足\(a\),\(b\)在同一个强联通分量中,那么此边是答案。
时间复杂度\(O(N+M)\)。
这道题使用匈牙利算法的思想,利用完备匹配时的特有性质,用Tarjan算法求解强联通分量,是道好题。

Code

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 4000 + 5;
const int M = 202000 + 5;
int n;
int head[N], nxt[M], ver[M], tot;
int dfn[N], low[N], id, c[N], cnt;
int stack[N], top, ins[N];
vector<int> ans;
void Add(int x, int y) {
	nxt[++tot] = head[x]; head[x] = tot; ver[tot] = y;
}
void Tarjan(int x) {
	dfn[x] = low[x] = ++id;
	stack[++top] = x; ins[x] = 1;
	for (int i = head[x]; i; i = nxt[i]) {
		int y = ver[i];
		if (!dfn[y]) {
			Tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if (ins[y]) low[x] = min(low[x], dfn[y]);
	}
	if (dfn[x] == low[x]) {
		int y; ++cnt;
		do {
			y = stack[top--]; ins[y] = 0; c[y] = cnt;
		} while (x != y);
	}
}
int main() {
	int m, x, y;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &m);
		while (m--) {
			scanf("%d", &y); Add(i, n + y);
		}
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", &x); Add(n + i, x);
	}
	for (int i = 1; i <= n; i++)
		if (!dfn[i]) {
			dfn[i] = 1; Tarjan(i);
		}
	for (int i = 1; i <= n; i++) {
		int s = 0; ans.clear();
		for (int j = head[i]; j; j = nxt[j])
			if (c[i] == c[ver[j]]) {
				s++; ans.push_back(ver[j]);
			}
		printf("%d ", s);
		sort(ans.begin(), ans.end());
		for (int j = 0; j < ans.size(); j++) printf("%d ", ans[j] - n);
		printf("\n");
	}
	return 0;
}
这篇关于Acwing411 国王的任务 题解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!