确定比赛名次
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6459 Accepted
Submission(s): 2451
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
给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。
其他说明:符合 条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
其他说明:符合 条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
Sample
Input
4 3
1 2
2 3
4 3
1 2
2 3
4 3
Sample Output
1 2 4 3
/* 有一段时间没好好写代码了,以后要好好学习天天向上,谁都别拦我。。哈哈 题意:拓扑排序 */ #include<cstdio> #include<cstring> #include<iostream> #define MAX 501 using namespace std; int map[MAX][MAX]; int indegree[MAX],ans[MAX]; void toposort(int n) //托扑排序 { int i,j,k; for(i=1;i<=n;++i) //从第一个节点开始找到一个节点入度为0的节点 { j=1; while(indegree[j]) ++j; ans[i]=j; indegree[j]=-1; //将已经找到的节点的入度更新为-1 for(k=1;k<=n;++k) //将所有与节点j相连的节点的入度值全部减1 { if(map[j][k]) --indegree[k]; } } } int main() { int n,m,a,b,i; while(~scanf("%d%d",&n,&m)) { memset(map,0,sizeof(map)); memset(indegree,0,sizeof(indegree)); while(m--) { scanf("%d%d",&a,&b); if(!map[a][b]) //注意本题会有重复输入,坑爹啊。。。。 ++indegree[b]; // 记录入度 map[a][b]=1; } toposort(n); for(i=1;i<n;++i) printf("%d ",ans[i]); printf("%d\n",ans[n]); } return 0; }
转载更健康。。。。
拓扑排序简单来说就是把一个图的所有节点排序,使得每一条有向边(u,v)对应的u都排在v的前面。
拓扑排序最大的用途就是判断一个有向图是否有环,当然判断还有一种方法就是Floyd算法。
如果用邻接表的话拓扑排序的时间复杂度是O(N*E),邻接矩阵是O(N^2),N表示顶点数,E表示边数,Floyd时间复杂度是O(N^3)。
性质
1、 拓扑排序在有向无环图中才能排出有效的序列,否则能判断该有向图有环。
2、如果输入的有向图中的点,不存在入度为0的点,则该有向图存在回路
3、如果存在的入度为0的点大于一个,则该有向图肯定不存在一个可以确定的拓扑序列但并不妨碍拓扑排序
下面给出核心程序:
vector<int>g[N];// 邻接表存储 int vis[N],topo[N],cnt; bool dfs(int u) { vis[u] = -1;//-1用来表示顶点u正在访问 for(int i = 0 ; i < g[u].size() ; i ++) { if(vis[g[u][i]] == -1)//表示这个点进入了两次,肯定出现了环 return false; else if(vis[g[u][i]] == 0) { dfs(g[u][i]); } } vis[u] = 1; topo[cnt++] = u;//放到结果数组里,输出的时候记得倒序输出,(回溯的原因) return true; } bool toposort(int n) { memset(vis,0,sizeof(vis)); for(int i = 1 ; i <= n ; i ++) { if(!vis[i]) { if(!dfs(i)) return false;//huan } } return true; } 非递归代码,这段代码无法判断是否有环 for(int i=1;i<=n;++i) for (int j=1;j<=n;++j) if (map[i][j]) into[j]++; for(i=1;i<=n;i++)//外层循环n次,into[]数组用来记录每个点的入度 { j=1; while(into[j]!=0) //从第一个节点开始找到一个节点入度为0的节点 j++; ans[i]=j;//存储答案 into[j]=-1;//将该节点的入度更新为-1 for( k=1;k<=n;k++)//将所有与节点j相连的节点的入度值全部减1 if(map[j][k]==1) into[k]--; }