拓扑排序及例题
拓扑排序解决排名和高低问题,给出多组关系,(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即可。
与上一题不同之处:
搜索顺序有变,用队列实现广搜(需要实现分层搜索,对每层有不同的处理)
需要记录处理了几个点,并将其作为搜索结束的判断标准(也能用来判断是否有换,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);
}
}
}更多推荐
所有评论(0)