HDU - 1285 确定比赛名次 (拓扑排序入门题目)

本文解析了一道经典的拓扑排序竞赛题,介绍如何通过构建有向无环图并运用拓扑排序算法来解决竞赛中的队伍排名问题。文章详细解释了拓扑排序的基本原理,并提供了一份AC代码作为示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有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

拓扑排序入门题。。。

题目输入给出多个队伍之间的排名前后关系,要求输出符合所给关系的排名。

前置知识:有向无环图必定存在拓扑序,可以进行拓扑排序找出其中满足条件的序列。

思考:何谓拓扑排序?我个人理解是按“度”排序。比如说,1队赢2队(有方向),那么我就将2队的度+1(可以理解为输的队伍的度+1)。;在排序的时候,度小的排在前面,每次找度为0的队伍,(度为0意味着该队伍在当前状态的图中没有队伍赢过他),然后该队伍就是当前状态图中最优的几个队伍中的一个队伍(可能同时有好几个度为0的队伍),也就是排名比其他队伍前!!!找到后就需要更新留下来的队伍的度(度为0的队伍都出去领奖了),输给出去领奖的队伍的队伍的度相应的减1(可以用邻接矩阵实现,也可以用vector,还有很多骚操作,条条大路通罗马)。至于为什么减1。。。因为输一次度就加一,赢你的队伍已经从图上暂时剔除了,所以度减1咯。。。

然后就是。。。重复上面的找度为零的操作。。。。。。。。。。。。。。。。。。。。。。。。。直到找到拓扑序。。。。。

有一个问题就是:如果在某个找度为零的过程找不到度为零的队伍呢? ans: 这就说明存在环!!!!!!存在环就没办法确定排名了,但是这道题已经说明一定存在解,所以不会出现环的情况。

附AC代码:

//#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<cstdio>
using namespace std;

#define up(i,j,k) for(int i=j;i<k;i++)
#define uup(i,j,k) for(int i=j;i>=k;i--)
#define mem(i,j) memset(i,j,sizeof i)
#define sfi(i) scanf("%d",&i)
#define sfl(i) scanf("%lld",&i)
#define sfd(i) scanf("%lf",&i)
#define sfc(i) scanf("%c",&i)
#define sfs(i) scanf("%s",i)
#define sf() cout<<"This is a flag!"<<endl;
#define wt(i) cout<<"This is "<<i<<endl;
#define ll long long
#define mod(x) (x)%mod

const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int MAX=5e2+50;

struct node
{
    int du;
    int t;
    int nxt[MAX];    //数据量比较小,所以可以随便构建结构体。。。只要保证实现能访问他的下一个就好。
}p[MAX];
bool mapp[MAX][MAX];   //判重,,其实不需要。。。重边在这里不影响。。。
bool vis[MAX];
int num[MAX];
int main()
{
    int n,m;
    //freopen("d:\\1.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        int a,b;
        mem(p,0);
        mem(mapp,0);
        up(i,0,m)
        {
            sfi(a);
            sfi(b);
            if(!mapp[a][b])
                mapp[a][b]=1;
            else
                continue;
            p[b].du++;
            p[a].nxt[p[a].t++]=b;
        }
        mem(vis,0);
        up(i,1,n+1)    //n个人。。
        {
            int xx=-1;
            up(j,1,n+1)
            {
                if(!vis[j]&&p[j].du==0)  //找到度为0的点
                {
                    xx=j;
                    break;
                }
            }
            if(xx==-1)  //存在环。。。
            {
                break;
            }
            vis[xx]=1;
            num[i]=xx;
            for(int j=0;j<p[xx].t;j++)   //更新与他相连的点的度
            {
                p[p[xx].nxt[j]].du--;
            }
        }
        up(i,1,n)
        {
            printf("%d ",num[i]);
        }
        printf("%d\n",num[n]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值