【Tarjan强连通分量】POJ 1904 King's Quest

这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!

一、题目大意

题目的大致意思是国王有N个儿子,也就是王子,每个王子有Ki个喜欢的姑娘,总共有N个姑娘,巫师给出了一种王子结婚的办法,国王觉得不是很好,要求求出每个王子可以结婚的姑娘有哪些,并且要保证每个王子都可以和自己喜欢的姑娘结婚。

二、题目思路以及AC代码

这道题,我承认,我不会。我还特地去学习了一些Tarjan求强连通分量是怎么写的。

但是看完题解,我就会了。这道题的大致思路就是,首先建图,然后求强连通分量,听起来也不是很难。关于建图,首先如果王子 u 喜欢某个姑娘 v,那么则有一条王子到姑娘的边(u, v),然后由于题目还给了一个巫师的标准答案,所以在标准答案中,如果王子 u 和某个姑娘 v 结婚,那么则有一条姑娘到王子的边(v, u),这样就建完图了,然后求解强连通分量就可以了,与王子在同一个强连通分量的姑娘则可以和王子结婚,且保证其他的王子也都可以和自己喜欢的姑娘结婚。

那是为什么呢?下面我就简单解释一下,因为巫师已经给出了一个正确的答案,那么我们就可以在这个正确答案的基础上进行修改,也就是换一换结婚的对象,只要保证,王子换一下,另一个王子同时可以有姑娘结婚就行了。首先对于给出的答案,相互结婚的王子 u 和姑娘 v 肯定是可以互相可达的,如果此时有另一个姑娘 p 和王子 u 在同一个强连通分量中,同时不要忘了,p 本来肯定是和一个王子结婚的,我们假设巫师给出和 p 结婚的王子为 w ,那么此时,至少 u、v、p、w 都是处于同一个强连通分量的,所以王子和这里的任意一个姑娘结婚,另一个王子都有相应的姑娘去结婚,也就是我们需要的结果。

这里要提醒一下,在实现的时候,千万不要把MAXN开成 2010,虽然N最大的2000,但建图的时候有最多2000个王子,2000个姑娘,所以要把MAXN开至少4010,不要学我…

下面给出AC代码:

#pragma warning(disable: 4996)

#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
#include <queue>
#include <functional>
#define MAXN 4010
using namespace std;

int n;
vector<int> edges[MAXN];

int k[MAXN];

// Tarjan
int dfn[MAXN], low[MAXN], vis[MAXN];
int index = 0;
stack<int> s;

// result
int belong[MAXN];
int scc = 0;

void init() {
	while (!s.empty()) s.pop();
	for (int i = 0; i < MAXN; i++) {
		edges[i].clear();
	}
}

void Tarjan(int u) {
	dfn[u] = low[u] = ++index;
	s.push(u);
	vis[u] = true;

	int size = edges[u].size();
	for (int i = 0; i < size; i++) {
		int v = edges[u][i];

		if (!dfn[v]) {
			Tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (vis[v]) {
			low[u] = min(low[u], dfn[v]);
		}
	}

	if (low[u] == dfn[u]) {
		int v;
		scc++;
		do {
			v = s.top(); s.pop();
			vis[v] = false;
			belong[v] = scc;
		} while (u != v);
	}
}

int main() {

	init();

	scanf("%d", &n);

	for (int i = 1; i <= n; i++) {
		scanf("%d", &k[i]);
		for (int j = 1; j <= k[i]; j++) {
			int v;
			scanf("%d", &v);
			edges[i].push_back(n + v);
		}
	}

	for (int i = 1; i <= n; i++) {
		int u;
		scanf("%d", &u);
		edges[n + u].push_back(i);
	}

	for (int i = 1; i <= 2 * n; i++) {
		if (!dfn[i])
			Tarjan(i);
	}

	for (int i = 1; i <= n; i++) {
		int size = edges[i].size();
		int cnt = 0;
		priority_queue<int, vector<int>, greater<int>> q;
		for (int j = 0; j < size; j++) {
			int v = edges[i][j];

			if (belong[i] == belong[v]) {
				cnt++;
				q.push(v);
			}
		}

		printf("%d ", cnt);
		while (!q.empty()) {
			printf("%d ", q.top() - n);
			q.pop();
		}
		printf("\n");
	}

	return 0;
}

如果有问题,欢迎大家指正!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值