拓扑排序解决排名和高低问题,给出多组关系,(x,y)表示x要排在y前面,最后输出排名顺序。

解决思路是将先后关系转化为边,用图来表示所有关系,每次找出入度为0的点进行处理,然后//删除从此点出发的边//,更新边终点的入度

例题1:确定比赛名次

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

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

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

#include<bits/stdc++.h>
using namespace std;
#define MAXN 500+7

vector<int> g[MAXN]; 
int n,m,x,y;
int indgr[MAXN];  //记录入度
int res[MAXN];    //记录最后排名

void init()
{
    memset(indgr,0,sizeof(indgr));
    memset(res,0,sizeof(res));
    for(int i=1;i<=n;i++)
    {
        g[i].clear();
    }
}

int main()
{
    while(cin>>n>>m)
    {
        init();
        while(m--)   //输入边
        {
            int bg=0;
            scanf("%d%d",&x,&y);
            for(int i=0;i<g[x].size();i++) //防止重边影响结果
            {
                if(g[x][i]==y) bg=1;break;
            }
            if(bg) continue;
            g[x].push_back(y);
            indgr[y]++;
        }
        
        //主要部分
        for(int i=1;i<=n;i++) //i代表第i名
        {
            for(int j=1;j<=n;j++) //按题目要求从小到大找
            {
                if(!indgr[j])
                {
                    indgr[j]--;   //删除点 
                    res[i]=j;     //记录点 
                    
                    for(int k=0;k<g[j].size();k++)
                    {
                        indgr[g[j][k]]--;
                    }
                    break;
                }
            }
        }
        
        for(int i=1;i<=n;i++)
        {
            if(i<n) printf("%d ",res[i]);
            else printf("%d\n",res[i]);
        }
        
    }
}

主要流程:1.输入边(注意防重边)

2.建立循环找入度为0的边,进行处理(包括删除点:j--,能到的点入度--, 题目要求的额外操作)

例题2:甘老板发红包

Problem Description

甘晨煜是一家软件公司的创始人,人称“甘老板”。

经过几年的努力,公司已经准备在纳斯达克上市,甘老板自然也是心情大好。随着中秋节的临近,甘老板决定为员工们每人发个红包。

现在的问题是,每人发多少红包呢?要知道,很多员工提出了自己的要求,比如,胡承轩就提出他的红包应该比麻致远的大!

为了图吉利,甘老板决定为每名员工至少发888的红包,同时,他还希望能满足员工们提出的所有的要求,当然,最后是希望发出红包的总金额最少。

Input

输入包含多组测试数据。

每组数据第一行首先是两个整数n和m,分别表示员工的人数是n,员工们一共提出了m条要求。

接着的m行,每行包含2个整数a和b,表示一条要求:a的红包应该比b的大。

n<=10000

m<=20000

员工编号a和b不等,且都在区间[1,n]内

Output

对于每组测试数据,请输出甘老板总共最少需要发出多少金额的红包。

如果不能满足员工提出的全部的要求,直接输出-1即可。

与上一题不同之处:

  1. 搜索顺序有变,用队列实现广搜(需要实现分层搜索,对每层有不同的处理)

  1. 需要记录处理了几个点,并将其作为搜索结束的判断标准(也能用来判断是否有换,cnt!=n)

#include<bits/stdc++.h>
using namespace std;
#define MAXN 10000+7

vector<int> g[MAXN]; 
int n,m,x,y;
int indgr[MAXN];
//int res[MAXN];
int ans,cnt;
queue<int> q;

void init()
{
    memset(indgr,0,sizeof(indgr));
//    memset(res,0,sizeof(res));
    for(int i=1;i<=n;i++)
    {
        g[i].clear();
    }
    ans=0;
    cnt=0;
}

int main()
{
    while(cin>>n>>m)
    {
        init();
        while(m--)
        {
            int bg=0;
            scanf("%d%d",&y,&x);
            for(int i=0;i<g[x].size();i++)
            {
                if(g[x][i]==y) bg=1;break;
            }
            if(bg) continue;
            g[x].push_back(y);
            indgr[y]++;
        }
        
        for(int i=0;i<n;i++)
        {
            for(int j=1;j<=n;j++)
            {
            
                if(!indgr[j])  //先找到但不能处理,处理后影响结果,只能找完这一层后再处理
                {
                    q.push(j);
                }
            }
            while(!q.empty())  //一定要用队列实现
            {
                int j=q.front();
                q.pop();
                indgr[j]--;   //删除点 
                ans+=i;    //每一层的工资值不同
                cnt++;
                
                for(int k=0;k<g[j].size();k++)
                {
                    indgr[g[j][k]]--;
                }
            }
            if(cnt==n) break;
        }
        
        if(cnt<n) printf("-1\n");
        else{
            printf("%d\n",ans+888*n);
        }
        
    }
}
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐