【hiho_1174】拓扑排序1


原题

https://hihocoder.com/problemset/problem/1174

输入

第1行:1个整数T,表示数据的组数T(1 <= T <= 5)
接下来T组数据按照以下格式:
第1行:2个整数,N,M。N表示课程总数量,课程编号为1…N。M表示顺序关系的数量。1 <= N <= 100,000. 1 <= M <= 500,000
第2…M+1行:每行2个整数,A,B。表示课程A是课程B的前置课程。

输出

第1…T行:每行1个字符串,若该组信息无误,输出"Correct",若该组信息有误,输出"Wrong"。


我的想法

邻接表存储有向图,然后在有向图中查找有无环存在。有些东西已不太熟,能想到的找环就是递归遍历看是否有重复节点,忒繁琐。


解析与思考

  1. 计算每一个点的入度值deg[i],这一步需要扫描所有点和边,复杂度O(N+M)。
  2. 把入度为0的点加入队列Q中,当然有可能存在多个入度为0的点,同时它们之间也不会存在连接关系,所以按照任意顺序加入Q都是可以的。
  3. 从Q中取出一个点p。对于每一个未删除且与p相连的点q,deg[q] = deg[q] - 1;如果deg[q]==0,把q加入Q。
  4. 不断重复第3步,直到Q为空。

最后剩下的未被删除的点,也就是组成环的点了。在这个过程中,需要注意对于有向图的构建,宜采用邻接表形式,以应对数据量大时的内存溢出问题。

常见的邻接表大多是使用的指针来进行元素的串联,其实我们可以通过数组来模拟这一过程。

int head[ MAXN + 1] = {0};	// 表示头指针,初始化为0
int p[ MAXM + 1];		// 表示指向的节点
int next[ MAXM + 1] = {0}; 	// 模拟指针,初始化为0
int edgecnt;			// 记录边的数量

void addedge(int u, int v) {	// 添加边(u,v)
	++edgecnt;
	p[ edgecnt ] = v;
	next[ edgecnt ] = head[u];
	head[u] = edgecnt;
}

// 枚举边的过程,u为起始点
for (int i = head[u]; i; i = next[i]) {
	v = p[i];
	...
}

看了解析之后的解答

#include <iostream>
#include <queue>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

#define MAXN 100000
#define MAXM 500000 

int main(int argc, char** argv) {
	int t = 0;
	int n = 0;
	int m = 0;
	std::queue<int> Q;
	scanf("%d", &t);
	while(t--) {
		int head[ MAXN + 1] = {0};	// 表示头指针,初始化为0
		int p[ MAXM + 1];			// 表示指向的节点
		int next[ MAXM + 1] = {0}; 	// 模拟指针,初始化为0
		int edgecnt = 0;				// 记录边的数量
		int deg[MAXN + 1] = {0};	// 记录每个点的入度值 
		
		scanf("%d %d", &n, &m);
		int u, v;
		while(m--) {
			scanf("%d %d", &u, &v);
			++edgecnt;
			p[ edgecnt ] = v;
			next[ edgecnt ] = head[u];
			head[u] = edgecnt;
		}
	//计算每一个点的入度值deg[i],这一步需要扫描所有点和边,复杂度O(N+M)。
		// 枚举边的过程,u为起始点
		for (u=1; u <= n; u++) {
			for (int i = head[u]; i; i = next[i]) {
				v = p[i];
				deg[v]++;
			}
		}
		
		
	// 把入度为0的点加入队列Q中,当然有可能存在多个入度为0的点,同时它们之间也不会存在连接关系,所以按照任意顺序加入Q都是可以的。
		for (int i = 1; i <= n; i++) {
			if (deg[i] == 0) {
				Q.push(i);
			}
		} 
		int left = n;
	// 从Q中取出一个点p。对于每一个未删除且与p相连的点q,deg[q] = deg[q] - 1;如果deg[q]==0,把q加入Q。
	// 不断重复第3步,直到Q为空。
		while (!Q.empty()) {
			int temp = Q.front();
			Q.pop();
			left--;
			for (int i = head[temp]; i; i = next[i]) {
				v = p[i];
				deg[v]--;
				if (deg[v] == 0) {
					Q.push(v);
				}
			}
		}
		if (left > 0) {
			printf("Wrong\n");
		}
		else {
			printf("Correct\n");
		}
	} 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值