1008. Airline Routes (35)解题报告

思路

我先通过DFS把图分解为若干个强连通分量,然后用并查集区分每个强连通分量。查询时,只要找并查集的根就可以了。这种方法来自《算法导论》22.5强连通分量。划分强连通分量的时间复杂度为O(V+E),查询的时间复杂度为O(K)。

 

  1. 用DFS标记出每个结点的结束时间。
  2. 按照结束时间降序,对逆邻接表进行DFS。
  3. 单独一次DFS所遍历的结点就是某一个强连通分量的所有成员。
  4. 在第二次DFS时,把同一个强连通分量的结点归并到同一个集合里。

 

在解题过程中,我也参阅了下面这篇博文:

PAT (Top Level) Practise 1008 Airline Routes (35) - 程序园

通过画面

 

代码

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
const int N = 10003;
void dfs(vector<int> city[], int index);
void dfs(vector<int> city[], int index, int cityset[]);
int findroot(int cityset[], int index);
void setunion(int cityset[], int index1, int index2);
stack<int> s;
bool visit[N];
int finish[N];
int main(void) {
	int n, m, k, i, source, dest, *cityset;
	vector<int> *city, *cityt;
	setvbuf(stdin, new char[1 << 20], _IOFBF, 1 << 20);
	setvbuf(stdout, new char[1 << 20], _IOFBF, 1 << 20);
	scanf("%d %d", &n, &m);
	cityset = new int[n + 1];
	city = new vector<int>[n + 1];
	cityt = new vector<int>[n + 1];
	for (i = 0; i < n + 1; i++) {
		cityset[i] = -1;
	}
	for (i = 0; i < m; i++) {
		scanf("%d %d", &source, &dest);
		city[source].push_back(dest);
		cityt[dest].push_back(source);
	}
	for (i = 1; i <= n; i++) {
		if (!visit[i]) {
			dfs(city, i);
		}
	}
	memset(visit, 0, N * sizeof(bool));
	for (i = n; i > 0; i--) {
		if (!visit[finish[i]]) {
			dfs(cityt, finish[i], cityset);
		}
	}
	scanf("%d", &k);
	for (i = 0; i < k; i++) {
		scanf("%d %d", &source, &dest);
		int root1 = findroot(cityset, source), root2 = findroot(cityset, dest);
		if (root1 != -1 && root1 == root2) {
			puts("Yes");
		}
		else {
			puts("No");
		}
	}
	delete[] city;
	delete[] cityt;
	delete[] cityset;
	return 0;
}

void dfs(vector<int> city[], int index, int cityset[]) {
	int i;
	visit[index] = true;
	s.push(index);
	for (i = 0; i < (int) city[index].size(); i++) {
		if (!visit[city[index][i]]) {
			setunion(cityset, index, city[index][i]);
			dfs(city, city[index][i], cityset);
		}
	}
	s.pop();
	return;
}

void dfs(vector<int> city[], int index) {
	int i;
	static int cnt = 0;
	visit[index] = true;
	s.push(index);
	for (i = 0; i < (int) city[index].size(); i++) {
		if (!visit[city[index][i]]) {
			dfs(city, city[index][i]);
		}
	}
	cnt++;
	finish[cnt] = index;
	s.pop();
	return;
}
int findroot(int cityset[], int index) {
	if (cityset[index] < 0) {
		return index;
	}
	else {
		return cityset[index] = findroot(cityset, cityset[index]);
	}
}
void setunion(int cityset[], int index1, int index2) {
	int root1, root2;
	root1 = findroot(cityset, index1);
	root2 = findroot(cityset, index2);
	if (root1 == root2) {
		return;
	}
	if (cityset[root1] < cityset[root2]) {
		cityset[root1] += cityset[root2];
		cityset[root2] = root1;
	}
	else {
		cityset[root2] += cityset[root1];
		cityset[root1] = root2;
	}
	return;
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值