13.拓扑排序

模板

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <queue>
using namespace std;
int inp[1010];	//入度
int n,m,cnt;
vector<int> v[1010];
int ans[1010]; 

bool bfs()
{
	
	queue<int> q;
	for(int i=1;i<=m;i++)	//i表示点的标号 
	{
		if(inp[i]==0)	//如果i的入度为0 
			q.push(i); 		//把它加入队列 
	}
	
	while(!q.empty())
	{
		int t=q.front();
		q.pop();	//队首出队列 
		ans[cnt++]=t;	//记录答案
		
		for(auto it:v[t])	//遍历出队列的那个点之后所有相连的点 
		{
			inp[it]--;	//入度减一
			if(inp[it]==0)	//如果入度减为0 
				q.push(it); 	//加入队列 
		} 
//		vector<int>::iterator it=v[t].begin();
//		for(it;it!=v[t].end();it++)
//		{
//			inp[*it]--;
//			if(inp[*it]==0)	q.push(*it);
//		}
	}
	
	if(cnt==m)	return 1;	//如果全部点都出队列,说明可以排序
	return 0; 
}

int main()
{
	
	cin>>n>>m;	//边数、点数
	
	for(int i=0;i<n;i++)
	{
		int a,b;
		cin>>a>>b;
		inp[b]++;	//b的入度++
		v[a].push_back(b);	//记录a的后续结点
	} 
	
	if(bfs())
	{
		cout<<ans[0];
		for(int i=1;i<m;i++)
			cout<<' '<<ans[i];
		cout<<endl;
	}
	else	cout<<-1<<endl;
	
	return 0;
}

练习

A - Genealogical tree(模板题)

The system of Martians’ blood relations is confusing enough. Actually, Martians bud when they want and where they want. They gather together in different groups, so that a Martian can have one parent as well as ten. Nobody will be surprised by a hundred of children. Martians have got used to this and their style of life seems to them natural.
And in the Planetary Council the confusing genealogical system leads to some embarrassment. There meet the worthiest of Martians, and therefore in order to offend nobody in all of the discussions it is used first to give the floor to the old Martians, than to the younger ones and only than to the most young childless assessors. However, the maintenance of this order really is not a trivial task. Not always Martian knows all of his parents (and there’s nothing to tell about his grandparents!). But if by a mistake first speak a grandson and only than his young appearing great-grandfather, this is a real scandal.
Your task is to write a program, which would define once and for all, an order that would guarantee that every member of the Council takes the floor earlier than each of his descendants.
Input
The first line of the standard input contains an only number N, 1 <= N <= 100 — a number of members of the Martian Planetary Council. According to the centuries-old tradition members of the Council are enumerated with the natural numbers from 1 up to N. Further, there are exactly N lines, moreover, the I-th line contains a list of I-th member’s children. The list of children is a sequence of serial numbers of children in a arbitrary order separated by spaces. The list of children may be empty. The list (even if it is empty) ends with 0.
Output
The standard output should contain in its only line a sequence of speakers’ numbers, separated by spaces. If several sequences satisfy the conditions of the problem, you are to write to the standard output any of them. At least one such sequence always exists.
Sample Input
5
0
4 5 1 0
1 0
5 3 0
3 0
Sample Output
2 4 5 3 1
题目大意:
输入n,后跟n列,分别对应从1到n号的所有儿子的标号,输出排序关系,辈分小的不能在大的前面。
AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
int n,cnt=0;
int inp[110];
vector<int> v[110];
int ans[110];

void bfs()
{
	queue<int> q;
	for(int i=1;i<=n;i++)
		if(inp[i]==0)	q.push(i);
		
	while(!q.empty())
	{
		int t=q.front();
		q.pop();
		ans[cnt++]=t;
		
		for(auto it:v[t])
		{
			inp[it]--;
			if(inp[it]==0)	q.push(it);
		}
	}
	
	cout<<ans[0];
	for(int i=1;i<n;i++)
		cout<<' '<<ans[i];
	cout<<endl;
}

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x;
		while(cin>>x&&x)
		{
			inp[x]++;
			v[i].push_back(x);
		}
	}
	bfs();
	return 0;
}

D - 产生冠军(set/拓扑)(思维)

有一群人,打乒乓球比赛,两两捉对撕杀,每两个人之间最多打一场比赛。
球赛的规则如下:
如果A打败了B,B又打败了C,而A与C之间没有进行过比赛,那么就认定,A一定能打败C。
如果A打败了B,B又打败了C,而且,C又打败了A,那么A、B、C三者都不可能成为冠军
根据这个规则,无需循环较量,或许就能确定冠军。你的任务就是面对一群比赛选手,在经过了若干场撕杀之后,确定是否已经实际上产生了冠军。
Input
输入含有一些选手群,每群选手都以一个整数n(n<1000)开头,后跟n对选手的比赛结果,比赛结果以一对选手名字(中间隔一空格)表示,前者战胜后者。如果n为0,则表示输入结束。
Output
对于每个选手群,若你判断出产生了冠军,则在一行中输出“Yes”,否则在一行中输出“No”。
Sample Input
3
Alice Bob
Smith John
Alice Smith
5
a c
c d
d e
b e
a d
0
AC代码
set版本:

#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#include <cmath>
#include <set>
using namespace std;
const int N=1010;
int inp[N];
int n;
string a,b;
set<string> s1,s2;
//s1存放所有选手名字,s2存放失败者的选手名字
//获胜者一定一次都没有失败过而且只有一个
//如果全部选手的数量减去所有有失败经历的选手是数量等于一,
//那么一定产生了冠军 
int main()
{
	while(cin>>n&&n)
	{
		s1.clear();
		s2.clear();
		int k=n;
		while(k--)
		{
			cin>>a>>b;
			s1.insert(a);
			s1.insert(b);
			s2.insert(b);
		}
		int sum=s1.size();
		int m=s2.size();
		if(sum-m==1)	cout<<"Yes"<<endl;
		else	cout<<"No"<<endl;
	}
	return 0;
}

拓扑❓❓版本:

//这道题不能通过判断是否可以拓扑排序来判断是否有冠军产生,因为在有环的情况下也可以产生冠军。
//比如,1、2、3形成个环,那么三者中不会产生冠军,如果此时4打败了3,那么4一定也可以打败1和2,所有4就是冠军。
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <map>
using namespace std;
int n;
string a,b;
map<string,int> mp;	//存放选手失败的次数 
int main()
{
	while(cin>>n&&n)
	{
		mp.clear();
		int cnt=0;	//记录从来没有 失败过的选手的数量 
		
		while(n--)
		{
			cin>>a>>b;
			mp[a]=mp[a];
			mp[b]++;
		}
		
		map<string,int>::iterator it;
		for(it=mp.begin();it!=mp.end();it++)
			if(it->second==0)	cnt++;
		
		if(cnt==1)	cout<<"Yes"<<endl;	//获胜者一定没有失败过而且只有1个 
		else	cout<<"No"<<endl;
	}
	return 0;
}

F - Reward⭐(逆向拓扑、BFS??)

Dandelion’s uncle is a boss of a factory. As the spring festival is coming , he wants to distribute rewards to his workers. Now he has a trouble about how to distribute the rewards.
The workers will compare their rewards ,and some one may have demands of the distributing of rewards ,just like a’s reward should more than b’s.Dandelion’s unclue wants to fulfill all the demands, of course ,he wants to use the least money.Every work’s reward will be at least 888 , because it’s a lucky number.
Input
One line with two integers n and m ,stands for the number of works and the number of demands .(n<=10000,m<=20000)
then m lines ,each line contains two integers a and b ,stands for a’s reward should be more than b’s.
Output
For every case ,print the least money dandelion ‘s uncle needs to distribute .If it’s impossible to fulfill all the works’ demands ,print -1.
Sample Input
2 1
1 2
2 2
1 2
2 1
Sample Output
1777
-1
AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int N=10100;
int n,m,inp[N],re[N],cnt;
vector<int> v[N];

void bfs()
{
	queue<int> q;
	ll sum=-1;
	cnt=0;
	for(int i=1;i<=n;i++)
	{
		if(inp[i]==0)
			q.push(i);
	}
	
	while(!q.empty())
	{
		int t=q.front();
		q.pop();
		cnt++;
		
		vector<int>::iterator it;
		for(it=v[t].begin();it!=v[t].end();it++)
		{
			inp[*it]--;
			if(inp[*it]==0)
			{
				q.push(*it);
				re[*it]=re[t]+1;	 
			}
		}
	}
	
	if(cnt==n)
	{
		sum=n*888;
		for(int i=1;i<=n;i++)
		{
			sum+=re[i];
		}
	}
	cout<<sum<<endl;
}

int main()
{
	while(cin>>n>>m)
	{
		memset(inp,0,sizeof(inp));
		memset(v,0,sizeof(v));
		memset(re,0,sizeof(re));
		while(m--)
		{
			int a,b;
			cin>>a>>b;
			inp[a]++;
			v[b].push_back(a);
		}
		bfs();
	}
	return 0;
}

H - Labeling Balls⭐⭐⭐(逆向拓扑)

Windy has N balls of distinct weights from 1 unit to N units. Now he tries to label them with 1 to N in such a way that:

No two balls share the same label.
The labeling satisfies several constrains like “The ball labeled with a is lighter than the one labeled with b”.
Can you help windy to find a solution?

Input
The first line of input is the number of test case. The first line of each test case contains two integers, N (1 ≤ N ≤ 200) and M (0 ≤ M ≤ 40,000). The next M line each contain two integers a and b indicating the ball labeled with a must be lighter than the one labeled with b. (1 ≤ a, b ≤ N) There is a blank line before each test case.

Output
For each test case output on a single line the balls’ weights from label 1 to label N. If several solutions exist, you should output the one with the smallest weight for label 1, then with the smallest weight for label 2, then with the smallest weight for label 3 and so on… If no solution exists, output -1 instead.

Sample Input
5

4 0

4 1
1 1

4 2
1 2
2 1

4 1
2 1

4 1
3 2
Sample Output
1 2 3 4
-1
-1
2 1 3 4
1 3 2 4

AC代码:

//这道题让输出的是标号1、2、3....对应的重量!!!不是排序后的标号!! 
//如果正向拓扑的话不能保证小标号拿到的体重尽可能小,因为最开始就把小的体重
//给用掉了了,如果后面有小标号也只能分再大一点的了 
//如果反向的话,先把大体重分给可以出去的标号中标号大的
//这样就能尽可能把小体重留给标号小的 
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int N=220;
int T,n,m,cnt;
int inp[N],ans[N];
vector<int> v[N];

void bfs()
{
	priority_queue<int> q;	//大标号在前,先给标号大的分配重量 
	for(int i=1;i<=n;i++)
	{
		if(!inp[i])	
			q.push(i);		
	}
	
	int k=n;
	while(!q.empty())
	{
		int t=q.top();
		q.pop();
		ans[t]=k--;		//记录重量 
		cnt++;	//记录已排好序的序号的个数 
		
		vector<int>::iterator it;
		for(it=v[t].begin();it!=v[t].end();it++)
		{
			inp[*it]--;
			if(!inp[*it])
				q.push(*it);
		}
	}
	
	if(cnt==n)
	{
		cout<<ans[1];
		for(int i=2;i<=n;i++)	cout<<' '<<ans[i];
		cout<<endl;
	}
	else	cout<<-1<<endl;
	return;
}

int main()
{
	cin>>T;
	while(T--)
	{
		cnt=0;
		memset(inp,0,sizeof(inp));
		memset(ans,0,sizeof(ans));
		memset(v,0,sizeof(v));
		
		cin>>n>>m;
		while(m--)
		{
			int a,b;
			cin>>a>>b;	
			inp[a]++;	//逆向拓扑 
			v[b].push_back(a);
		}
		bfs();
	}
	return 0;
}
/* 
5 4
5 1
4 2
1 3
2 3
正向:4 2 5 1 3
逆向:2 4 5 3 1
*/

I - Cow Contest(floyd)⭐⭐

FJ的N(1 <= N <= 100)头奶牛们最近参加了场程序设计竞赛:)。在赛场上,奶牛们按1…N依次编号。每头奶牛的编程能力不尽相同,并且没有哪两头奶牛的水平不相上下,也就是说,奶牛们的编程能力有明确的排名。 整个比赛被分成了若干轮,每一轮是两头指定编号的奶牛的对决。如果编号为A的奶牛的编程能力强于编号为B的奶牛(1 <= A <= N; 1 <= B <= N; A != B) ,那么她们的对决中,编号为A的奶牛总是能胜出。 FJ想知道奶牛们编程能力的具体排名,于是他找来了奶牛们所有 M(1 <= M <= 4,500)轮比赛的结果,希望你能根据这些信息,推断出尽可能多的奶牛的编程能力排名。比赛结果保证不会自相矛盾。
Input
第1行: 2个用空格隔开的整数:N 和 M 第2…M+1行: 每行为2个用空格隔开的整数A、B,描述了参加某一轮比赛的奶 牛的编号,以及结果(编号为A,即为每行的第一个数的奶牛为 胜者)
Output
第1行: 输出1个整数,表示排名可以确定的奶牛的数目
Sample Input
5 5
4 3
4 2
3 2
1 2
2 5
Sample Output
2
AC代码:
好像可以用拓扑写⑧,可是我不会…

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=110;
int n,m;
int dis[N][N],cnt[N];

void floyd()
{
	//记录由传递性产生的关系 
	for(int k=1;k<=n;k++)	//中间点 
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(dis[i][k]==1&&dis[k][j]==1)	//如果i和k有关系、k和j有关系 
					dis[i][j]=1;	//那么i和j也有关系 
			}
		}
	}
}

int main()
{
	memset(cnt,0,sizeof(cnt));
	memset(dis,0,sizeof(dis));
	cin>>n>>m;
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		dis[a][b]=1;	//a、b有关系 
	}
	floyd();
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if((dis[i][j]||dis[j][i]==1)&&(i!=j))
				cnt[i]++;	//记录与i有关系的点数 
		}
		if(cnt[i]==n-1)	ans++;
		//如果其余所有点都和i有关系,那么i的排名可以确定 
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值