拓扑排序

在一个表示工程的有向图中,如果顶点表示活动,有向边表示活动的优先关系,这种有向图叫做顶点表示活动的优先次序的网络,简称为AOV网。如果一个AOV网可以进行拓扑排序,则这个工程是可以顺利进行的。

拓扑排序是确定AOV网拓扑序列的一种排序方法,AOV网的拓扑序列并不是唯一的。

实现的基本思想也比较简单:使用一个容器维护每个点的信息,包含其入度数和所有后继点信息,将所有入度数为零的点压入队列(或者使用栈)并标记次序。当点从队列弹出时,相当于把该点从图中删除,该点的所有后继点的入度数-1。重复再将新的入度为零的点压入队列重复操作直到所有点都进入过队列,标记的次序即为拓扑排序。

需要将问题抽象成图中的n个点和e条有向边,那么其总的时间复杂度为O(n+e)

明显可知如果图中带有环则有些点是不会进入队列的,因此如果排序的点数小于总数则说明图中有环,无法进行拓扑排序!

当所给信息不完全时拓扑排序的次序可能不是唯一的。可以观察队列的点的个数,如果存在某个时刻队列中的点个数超过1,那么这些点之间的次序是无法确定。

确定比赛名次

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 36851    Accepted Submission(s): 14401

Problem Description

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

Input

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

Output

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

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

Sample Input

4 3 1 2 2 3 4 3

Sample Output

1 2 4 3

基本的拓扑排序,需要注意的是当不唯一时需要按队号从小到大排序,那么可以使用优先队列确保压入队列之间相互关系不确定的点是按升序排列的。

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<functional>
using namespace std;
int n, m;
int ans[501], res[501];
vector<int> team[501];         //用容器维护,该容器的每一个元素都是一个数组
void topsort()
{
    priority_queue<int, vector<int>, greater<int> > q;    //优先队列升序排序
    int num = 0;
    for (int i = 1; i <= n; i++)
    {
        if (team[i][0] == 0)
            q.push(i);
    }
    while (!q.empty())
    {
        int tmp = q.top();
        q.pop();
        ans[tmp] = ++num;      //ans数组角标号即队号,记录每个队的名次
        for (int i = 1; i <team[tmp].size(); i++)
        {
            if (--team[team[tmp][i]][0] == 0)  //后继点入度减一,判断是否可进入队列
                q.push(team[tmp][i]);
        }
    }
    for (int i = 1; i <= n; i++)
        res[ans[i]] = i;
}
int main()
{
    while (scanf("%d%d", &n, &m) != EOF)
    {
        for (int i = 1; i <= n; i++)          //容器初始化,注意容器team的大小为501,但每个元素的大小为零,需要pushback而不能直接使用空间!
        {
            team[i].clear();
            team[i].push_back(0);
        }
        for (int i = 1; i <= m; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            team[b][0]++;
            team[a].push_back(b);
        }
        topsort();
        for (int i = 1; i < n; i++)
            cout << res[i] << ' ';
        cout << res[n] << endl;
    }
    return 0;
}

Rank of Tetris

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 12088    Accepted Submission(s): 3434

Problem Description

自从Lele开发了Rating系统,他的Tetris事业更是如虎添翼,不久他遍把这个游戏推向了全球。

为了更好的符合那些爱好者的喜好,Lele又想了一个新点子:他将制作一个全球Tetris高手排行榜,定时更新,名堂要比福布斯富豪榜还响。关于如何排名,这个不用说都知道是根据Rating从高到低来排,如果两个人具有相同的Rating,那就按这几个人的RP从高到低来排。

终于,Lele要开始行动了,对N个人进行排名。为了方便起见,每个人都已经被编号,分别从0到N-1,并且编号越大,RP就越高。
同时Lele从狗仔队里取得一些(M个)关于Rating的信息。这些信息可能有三种情况,分别是"A > B","A = B","A < B",分别表示A的Rating高于B,等于B,小于B。

现在Lele并不是让你来帮他制作这个高手榜,他只是想知道,根据这些信息是否能够确定出这个高手榜,是的话就输出"OK"。否则就请你判断出错的原因,到底是因为信息不完全(输出"UNCERTAIN"),还是因为这些信息中包含冲突(输出"CONFLICT")。
注意,如果信息中同时包含冲突且信息不完全,就输出"CONFLICT"。

Input

本题目包含多组测试,请处理到文件结束。
每组测试第一行包含两个整数N,M(0<=N<=10000,0<=M<=20000),分别表示要排名的人数以及得到的关系数。
接下来有M行,分别表示这些关系

Output

对于每组测试,在一行里按题目要求输出

Sample Input

3 3 0 > 1 1 < 2 0 > 2

4 4 1 = 2 1 > 3 2 > 0 0 > 1 3 3 1 > 0 1 > 2 2 < 1

Sample Output

OK

CONFLICT

UNCERTAIN

并查集+拓扑排序。问题在于处理等号,如果相等则将两数合并,所有相等的数均由一个代表元表示。

#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int N = 10005;
int n, m, f[N], X[2 * N], Y[2 * N], t;
char O[2 * N];
vector<int> G[N];

void initSet(int n)                   //初始化
{
	for (int i = 0; i <= n; ++i)
		f[i] = i;
	for (int i = 0; i <= n; ++i)
	{
		G[i].clear();
		G[i].push_back(0);
	}
}
int find(int x) {                     //查找操作
	if (f[x] == x)
		return x;
	return f[x] = find(f[x]);
}
bool merge(int x, int y)              //合并操作,如果和并成功那么一个数由其代表元完全替代,总数需要减一
{
	int a = find(x), b = find(y);
	if (a == b) {
		return false;
	}
	f[a] = b;
	return true;
}
int main() {
	int u, v;
	char ch;
	while (scanf("%d%d", &n, &m)!=EOF) {
		initSet(n);
		int num = n;
		for (int i = 0; i < m; ++i) {
			scanf("%d %c %d", &X[i], &O[i], &Y[i]);
			if (O[i] == '=') {           //等号就先全合并
				if (merge(X[i], Y[i]))
					--num;
			}
		}
		for (int i = 0; i < m; ++i)
			if (O[i] != '=')             //依据大小关系转换为图的关系
			{
				int x = find(X[i]), y = find(Y[i]);
				if (O[i] == '>')
				{
					G[y][0]++;
					G[x].push_back(y);
				}
				else
				{
					G[x][0]++;
					G[y].push_back(x);
				}
			}
		queue<int>q;
		for (int i = 0; i < n; ++i) {
			if (G[i][0] == 0 && i == find(i))
				q.push(i);
		}
		int stan = 0;
		while (!q.empty())
		{
			if (q.size() > 1) stan = 1;      //说明排序不唯一了
			int t = q.front();
			q.pop();
			--num;
			for (int v = 1; v < G[t].size(); ++v) {
				if (--G[G[t][v]][0] == 0)
					q.push(G[t][v]);
			}
		}
		if (num > 0)
			printf("CONFLICT\n");
		else if (stan)
			printf("UNCERTAIN\n");
		else
			printf("OK\n");
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值