HDU 1285 确定比赛名次(拓扑排序模板)

时间:2023-03-08 23:51:10
HDU 1285 确定比赛名次(拓扑排序模板)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1285

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

解题思路:拓扑排序裸题,但是有要求并列的点序号小的排在前面。开始写了一个dfs版倒序的怎么写都错,后来发现根本做不了(可能是我太菜了)。。。。于是改写了一个通过每次查找入读为0的点,然后删除有关边的版本,一下就AC了。

先是对了的方法,先叫它减入度法吧,这种做法可以判断有向图是否成单链,也就是没有分支,若一次找到入度为0的点有两个则存在分支。

代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=5e2+; int n,m;
int degree[N];
bool G[N][N];
queue<int>q; void toposort(){
for(int i=;i<=n;i++){
//寻找入度为0的点
int j=;
while(degree[j]!=) j++;
degree[j]--;
q.push(j);
//将关联的点的入度减1,即删除与该节点关联的边
for(int k=;k<=n;k++){
if(G[j][k])
degree[k]--;
}
}
} int main(){
while(~scanf("%d%d",&n,&m)){
memset(G,false,sizeof(G));
memset(degree,,sizeof(degree));
for(int i=;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
if(!G[a][b]){
G[a][b]=true;
degree[b]++;
}
}
toposort();
while(!q.empty()){
if(q.size()==)
printf("%d\n",q.front());
else
printf("%d ",q.front());
q.pop();
}
}
return ;
}

然后是dfs版的,虽然不能写这题,但还是当个模板放着吧,dfs法可以在O(n^2)时间内判断是否有环,而floyd需要O(n^3)。

代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
const int N=5e2+; int n,m;
int G[N][N],vis[N];//vis[i]=0,-1,1分别表示未访问、正在访问、已访问并且已递归访问完所有子孙
stack<int>res; bool dfs(int u){
vis[u]=-;
for(int i=;i<=n;i++){
if(G[u][i]){
if(vis[i]<) return false;
else if(!vis[i]&&!dfs(i))
return false;
}
}
vis[u]=;
res.push(u);
return true;
} bool toposort(){
memset(vis,,sizeof(vis));
for(int i=;i<=n;i++){
if(!vis[i]){
if(!dfs(i)) return false;
}
}
return true;
} int main(){
while(~scanf("%d%d",&n,&m)){
memset(G,,sizeof(G));
for(int i=;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
G[a][b]=;
}
toposort();
while(!res.empty()){
if(res.size()==)
printf("%d\n",res.top());
else
printf("%d ",res.top());
res.pop();
}
}
return ;
}