拓补排序

突然发现之前做图论的题的时候忘记发博客了。。。今天在做map的时候突然发现

拓补序列的特征:若节点U经过若干条有向边后能够到达节点V,则在求得的序列中U必然排在V之前

求拓补序列的方法:选择入度为0的节点,作为序列的第一个节点,当该节点被选为序列的第一个顶点之后,将该点从图中删去,同时删去以该节点为弧尾的所有边,得到一个新图。重复找入度为0的点删去,若到了最后出现了找不到入度为0 的点的情况,说明有环路,不存在拓补序列

例一:九度1448

题目描述:

ACM-DIY is a large QQ group where many excellent acmers get together. It is so harmonious that just like a big family. Every day,many "holy cows" like HH, hh, AC, ZT, lcc, BF, Qinz and so on chat on-line to exchange their ideas. When someone has questions, many warm-hearted cows like Lost will come to help. Then the one being helped will call Lost "master", and Lost will have a nice "prentice". By and by, there are many pairs of "master and prentice". But then problem occurs: there are too many masters and too many prentices, how can we know whether it is legal or not?We all know a master can have many prentices and a prentice may have a lot of masters too, it's legal. Nevertheless,some cows are not so honest, they hold illegal relationship. Take HH and 3xian for instant, HH is 3xian's master and, at the same time, 3xian is HH's master,which is quite illegal! To avoid this,please help us to judge whether their relationship is legal or not. Please note that the "master and prentice" relation is transitive. It means that if A is B's master ans B is C's master, then A is C's master.

输入:

The input consists of several test cases. For each case, the first line contains two integers, N (members to be tested) and M (relationships to be tested)(2 <= N, M <= 100). Then M lines follow, each contains a pair of (x, y) which means x is y's master and y is x's prentice. The input is terminated by N = 0.TO MAKE IT SIMPLE, we give every one a number (0, 1, 2,..., N-1). We use their numbers instead of their names.

输出:

For each test case, print in one line the judgement of the messy relationship.If it is legal, output "YES", otherwise "NO".

样例输入:
3 2
0 1
1 2
2 2
0 1
1 0
0 0
样例输出:
YES
NO

#include<stdio.h>
#include<vector>
#include<queue>
using namespace std;
vector <int> edge[1000];
queue<int> q;
int main() {
	int n, m;
	int i, j;
	int indegree[1000]; //表示一个节点的入度
	while (scanf("%d%d", &n, &m) != EOF) {
		if (m == 0 && n == 0) break;
		for (i = 0; i < 1000; i++) {
			edge[i].clear();
			indegree[i] = 0;
		}
		while (m--) {
			int a, b;
			scanf("%d%d", &a, &b);
			edge[a].push_back(b); 
			indegree[b]++;  //弧尾点的入度加一
		}
		//清空队列里的所有元素
		while(q.empty()==false){
			q.pop();
		}
		//找出所有入度为0的点存入队列
		for (i = 0; i < n; i++) {
			if (indegree[i] == 0) {
				q.push(i);
			}
		}
		//将入度为0的点依次读出
		int cnt=0; 
		while (q.empty() == false) {
			int t = q.front();
			q.pop();
			cnt++; //表示确定了的节点
			//把入度为0的点去掉之后,把链表中涉及到的点入度减一
			for (i = 0; i < edge[t].size(); i++) {
				indegree[edge[t][i]]--;
				if (indegree[edge[t][i]] == 0) {
					q.push(edge[t][i]);
				}
			}
		}
		if (cnt == n) {
			printf("YES\n");
		}
		else {
			printf("NO\n");
		}
	}
	return 0;
}

例二:九度1449

题目描述:

有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。

输入:

输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。

输出:

给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。

其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。

样例输入:
4 3
1 2
2 3
4 3
样例输出:
1 2 4 3

#include<stdio.h>
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<functional>
using namespace std;
vector<int> edge[1000];
priority_queue <int, vector<int>, greater<int> > q;

int main() {
	int n, m;
	int i, j;
	int indegree[1000];
	while (scanf("%d%d", &n, &m) != EOF) {
		for (i = 1; i <= n; i++) {
			indegree[i] = 0;
			edge[i-1].clear();
		}
		while (m--) {
			int a, b;
			scanf("%d%d", &a, &b);
			edge[a].push_back(b);
			indegree[b]++;
		}
		//清空队列中残余的点
		while (q.empty() == false) {
			q.pop();
		}
		//找出所有入度为0的存入队列
		for (i = 1; i <= n; i++) {
			if (indegree[i] == 0) {
				q.push(i);
			}
		}
		//从队头开始依次去除入度为0的点
		int ans[1000];
		int size=0;
		while (q.empty() == false) {
			int t = q.top(); //可以保证序号从大到小 符合题目要求
			ans[size++] = t;
			q.pop();
			//将链表中涉及到的节点入度减一
			for (i = 0; i < edge[t].size(); i++) {
				indegree[edge[t][i]]--;
				if (indegree[edge[t][i]] == 0) {
					q.push(edge[t][i]);
				}
			}
		}
		for (i = 0; i < size; i++) {
			printf("%d", ans[i]);
		}
	}
}

例三:九度1450 产生冠军(利用map!只需判断是不是只有一个度为0的点即可)

#include<stdio.h>
#include<map>
#include<string>
#include<queue>
using namespace std;
map<string, int> M;
int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		char str1[10], str2[10];
		int indegree[2002];
		string a, b;
		int ta, tb;
		int t=0;
		M.clear();
		for (int i = 0; i < 2 * n; i++) {
			indegree[i] = 0;
		}
		while (n--) {
			scanf("%s %s", str1, str2);
			a = str1;
			b = str2;
			if (M.find(a) == M.end()) {
				M[a] = t;
				t++;
			}
			else {
				ta = M[a];
			}

			if (M.find(b) == M.end()) {
				M[b] = t;
				indegree[t]++;
				t++;
			}
			else {
				tb = M[b];
				indegree[tb]++;
			}
		}
		int flag = 0;
		for (int i = 0; i < t; i++) {
			if (indegree[i] == 0) {
				flag++;
			}
		}
		if (flag == 1) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

题目描述:

有一群人,打乒乓球比赛,两两捉对撕杀,每两个人之间最多打一场比赛。
球赛的规则如下:
如果A打败了B,B又打败了C,而A与C之间没有进行过比赛,那么就认定,A一定能打败C。
如果A打败了B,B又打败了C,而且,C又打败了A,那么A、B、C三者都不可能成为冠军。
根据这个规则,无需循环较量,或许就能确定冠军。你的任务就是面对一群比赛选手,在经过了若干场撕杀之后,确定是否已经实际上产生了冠军。

输入:

输入含有一些选手群,每群选手都以一个整数n(n<1000)开头,后跟n对选手的比赛结果,比赛结果以一对选手名字(中间隔一空格)表示,前者战胜后者。如果n为0,则表示输入结束。

输出:

对于每个选手群,若你判断出产生了冠军,则在一行中输出“Yes”,否则在一行中输出“No”。

样例输入:
3
Alice Bob
Smith John
Alice Smith
5
a c
c d
d e
b e
a d
0
样例输出:
Yes
No


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值