拓扑排序例题(学习笔记)

拓扑排序算法例题

1. HDU 3342 ---- 有向图判环

Problem Description
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.

Input
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.

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

Sample Input

3 2
0 1
1 2
2 2
0 1
1 0
0 0

Sample Output

YES
NO

#include <iostream>
#include <vector>
#include <queue>
#include <cstdio>
#include <string.h>
using namespace std;

vector<int> stud[102]; //一维数组中的元素是向量,整体则是二维数组 即:stud[0],stud[1],stud[2]...都是向量
int indegree[102];     //记录各顶点入度
int n,m;
queue<int> q;          //记录拓扑排序出来的排列

void Init()
{   //initialize

    int i;
    int x,y;
    memset(indegree,0,sizeof(indegree));
    for(i=0 ; i<102 ; i++)
    {   //记得初始化一维数组(里面储存的是向量)
        //没有初始化就会WA
        stud[i].clear();
    }
    for(i=0 ; i<m ; i++)
    {
        scanf("%d%d",&x,&y);
        stud[x].push_back(y); //在向量stud[x]的末尾添加y
        indegree[y]++;        //y的入度加1
    }
}

int topo_sort()  //拓扑排序,本次排序的任务是判断最终是否会成环
{
    for(int i=0 ; i<n ; i++)
        if(indegree[i]==0) q.push(i);
    int cnt=0; //cnt记录取了几次队首元素,用于判断是否有环
    while(!q.empty())
    {
        cnt++;  //非空即取
        int tmp=q.front();
        q.pop();
        for(int i=0 ; i<stud[tmp].size() ; i++)
        {
            int after=stud[tmp][i];  //寻找此点的所有直接后继
            indegree[after]--;       //让它们的入度-1,因为此点已经出队了,指向每个后继结点的线段就少了一条
            if(indegree[after]==0)
                q.push(after); //如果某个后继结点的入度减到0了,直接入队
        }
    }
    if(cnt<n)
        return -1; //cnt<n,有的结点没有入队,即数据结构中存在环
    else
        return 0;   //正常
}

int main()
{

    scanf("%d%d",&n,&m);
    while(n!=0)
    {
        Init();
        if(topo_sort()==0)
            printf("YES\n");
        else
            printf("NO\n");
        scanf("%d%d",&n,&m);
    }
    return 0;
}

2. HDU 1285 ---- 确定比赛名次

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 <vector>
#include <cstdio>
#include <queue>
#include <string.h>
#include <functional>
using namespace std;
int n,m;
vector<int> team[505];
int indegree[505];
//优先队列



void Init()
{
    int i;
    for(i=1 ; i<=n ; i++)
        team[i].clear();
    memset(indegree,0,sizeof(indegree));
    int win,lose;
    for(i=1 ; i<=m ; i++)
    {
        scanf("%d%d",&win,&lose);
        team[win].push_back(lose);
        //indegree[win]++;额打错了
        indegree[lose]++;
    }
}

void topoSort()
{
    priority_queue<int,vector <int> ,greater <int> >q; //记得加functional头文件
    int i,j;
    //找入度为0的结点,让它们入队
    for(i=1 ; i<=n ; i++)
        if(indegree[i]==0)
            q.push(i);
    //int cnt=0;
    while(!q.empty())
    {
        int tmp=q.top();
        q.pop();
        //cnt++;
        for(j=0 ; j<team[tmp].size() ; j++)
        {   //j必须从0开始,因为访问向量中的元素是要用下标
            int loser=team[tmp][j];
            indegree[loser]--;
            if(indegree[loser]==0)
                q.push(loser);
        }
        if(!q.empty())
            printf("%d ",tmp);
        else
            printf("%d\n",tmp);
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        Init();
        topoSort();
    }
    return 0;
}

3.(未完成)POJ 3687 ---- Labeling Balls

Description

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

这题题意一定要看明白,特别绕

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值