题目1449:确定比赛名次
时间限制:1 秒内存限制:128 兆特殊判题:否提交:669解决:293
题目描述:
有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。
输入:
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。
输出:
给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
样例输入:
4 3
1 2
2 3
4 3
样例输出:
1 2 4 3
//拓扑排序
/*拓扑排序方法如下:
(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它.
(2)从网中删去该顶点,并且删去从该顶点发出的全部有向边.
(3)重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止.*/
#include<stdio.h> #include<string.h> int degree[510],bin[510],map[510][510]; void ToPu(int n) { int i,j,p; for(i=1;i<=n;i++) { p=-1;//用来记录入度为0的结点 for(j=1;j<=n;j++) { if(degree[j]==0)//如果入度为0 { degree[j]--;//让他为负数 bin[i]=p=j; break;//找到那个入度为0的结点,说明他是本组查找的元老 } } for(j=1;j<=n;j++) { if(map[p][j]==1)//去掉p链接的所有路 { map[p][j]=0; degree[j]--;//j的入度减去1 } } } } int main() { int i,j,n,m,x,y; while(scanf("%d %d",&n,&m)!=EOF) { memset(degree,0,sizeof(degree)); memset(bin,0,sizeof(bin)); memset(map,0,sizeof(map)); for(i=0;i<m;i++) { scanf("%d %d",&x,&y); if(map[x][y]==0) { map[x][y]=1; degree[y]++;//y点的入度加一 } } ToPu(n); printf("%d",bin[1]); for(i=2;i<=n;i++) printf(" %d",bin[i]); puts(""); } return 0; }
因为每次都要搜索入度为0的节点,花费的时间是O(n^2)
可以进行优化到O(E+V)
优化思想,可以通过将所有(未分配拓扑编号)入度为0的顶点放在一个特殊的盒子中而避免
这种无效的劳动。当降低邻接顶点的入度的时候,检查每一个顶点并在他入度为0的时候把它放入盒子。
步骤:
1.对每一个顶点计算它的入度,将所有入度为0的顶点放入一个初始为空的队列中。
2.当队列不空时,删除一个顶点v,并将与v邻接的所有的顶点的入度减1。
3.在刚刚的操作中,只要有一个顶点的入度降为0,就把该顶点放入队列中。
此时,拓扑排序就是顶点出队的顺序。
优化代码:
//注意,这个写法是没要求输出时编号小的队伍在前
//写这个的目的是清楚优化的方法,不是为此题而编写
void ToPu(int n) { queue<int> q; int sum=1; int v,i; while(!q.empty()){q.pop();} for(i=1;i<=n;i++) if(degree[i]==0) q.push(i); while(!q.empty()) { v=q.front(); q.pop(); bin[sum++]=v; for(i=1;i<=n;i++) { if(map[v][i]==1) { degree[i]--; if(degree[i]==0) q.push(i); } } } }