hdu 1285 确定比赛名次
//拓扑排序裸题,题目要求按编号从小到大输出,要换一种思维去思考
//按照数据结构课本的算法,建立邻接表,用栈实现。初始化先将入度为0的顶点入栈,然后以栈顶顶点为准,先将栈顶顶点出栈输出它的信息,然后扫描它的邻接表,没找到一个弧头就将其入度减1,减1后判断该弧头是否入度为0,是的话就入栈。直到所有顶点都出栈那么就输出了一个拓扑序列。但是这样做不能保证编号从小到大输出
为了保证从小到大输出,要用“每次都从头来过”的思想,因为题目保证一定存在拓扑序列,所以对于n个点,一定输出n次,所有外循环是次数,找到了n次,输出n个点,然后每次找,都是按照编号从1找到n,所以内循环是对应顶点的编号,从1到n,从当找到一个顶点,还没有纳入拓扑序列并且入度为0,就输出这个顶点的信息,并且扫描它的邻接表,将所有的弧头的入度都减1,然后再从头找过
所以找了n次,从次都是从编号1到n地扫描所有顶点,这样就能保证拓扑序列的正确性,而且因为编号都是从小到大扫描的,那么保证了先输出了编号小的
//用数组来实现邻接表
#include <stdio.h> #include <string.h> #define MAXN 510 #define MAXM 250100 bool g[MAXN][MAXN],vis[MAXN]; //g对应边集数组,体重可能有重复,vis记录那些顶点已经纳入序列 int u[MAXM],v[MAXM]; //保存弧u->v int first[MAXN]; //对应每个顶点的邻接表的第一条弧的编号 int in[MAXN]; //对应每个顶点的入度 int nextt[MAXM]; //对应一条弧的下一弧的编号 int n,m; void input() { int i,j,k; memset(first , -1, sizeof(first)); memset(in,0,sizeof(in)); memset(g,0,sizeof(g)); for(i=0; i<m; i++) //对应m条弧 { scanf("%d%d",&u[i] , &v[i]); if(g[u[i]][v[i]]) continue; g[u[i]][v[i]]=1; in[v[i]]++ ; nextt[i]=first[u[i]]; first[u[i]]=i; } return ; } /* void print_graph() { int i,j,k,t; for(i=1; i<=n; i++) //对应图的n个顶点,是从1开始标号的 { printf("顶点%d:",i); k=first[i]; //得到这个顶点的第一条弧 while(k!=-1) { printf(" %d",v[k]); k=nextt[k]; } printf("另外入度为%d\n",in[i]); } return ; } */ void topsort() { int i,j,k,t,f; memset(vis , 0 ,sizeof(vis)); for(f=0,i=1; i<=n; i++) //从头找n次,输出全部n个顶点 { for(j=1; j<=n; j++) //对应n个顶点的标号 if(!vis[j] && in[j]<=0) //zhe点还没有输出,而且这个点的入度为0,那么就是要输出这个点 break; vis[j]=1; if(!f) printf("%d",j) , f=1; else printf(" %d",j); t=first[j]; //找到第j个顶点的邻接表,找到它的第1条弧 while(t!=-1) { k=v[t]; //找到弧头元素 in[k]--; //弧头元素的入度减1 t=nextt[t]; //第k条弧的下一条弧j } } printf("\n"); return ; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { input(); //输入,建图 // print_graph(); topsort(); } return 0; }