杭州电子科技大学 Online Judge 之 “确定比赛名次(ID1285)”解题报告
杭州电子科技大学Online Judge 之 “确定比赛名次(ID1285)”解题报告巧若拙(欢迎转载,但请注明出处:http://blog.)
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
Sample Output
1 2 4 3
算法分析:
本题是拓扑排序的典型应用。
由于顶点数量不多,可以采用邻接矩阵来存储图信息,这样算法比较简单,只需要搜索n次,每次把序号最小的入度为0的顶点存储到拓扑序列中就行了。算法思路比较清晰,代码也比较简洁,但时间复杂度和空间复杂度都较高。
另一种方法是采用邻接表存储图信息。由于题目要求输出时编号小的队伍在前,所以在入栈时一定要保证先让序号最小的入度为0的顶点在栈顶,这样根据后进先出的特点,可以把序号最小的顶点存储到拓扑序列中。我采用折半插入排序的方法,把入度为0的顶点按递减序排序,然后对图进行深度优先搜索,能获得正确的拓扑序列。本算法时间复杂度和空间复杂度都很好,但是代码较长。
两种算法都给出代码,大家可以比较一下,并请提出宝贵意见。
说明:
算法思想:拓扑排序,折半插入。
数据结构:邻接矩阵,邻接表。
时间复杂度:算法1:O(N^2);其中N为顶点数量;
算法2:O(N+M);其中N为顶点数量;M为边的数量
空间复杂度:算法1:O(MAXN^2);其中MAXN为最大顶点数量;
算法2:O(MAXN +M);其中MAXN为最大顶点数量;M为边的数量。
Run ID Submit Time Judge Status Pro.ID Exe.Time Exe.Memory Code Len. Language Author
12236157 2014-11-19 14:09:06 Accepted 1285
31MS 1232K 1128 B
C 巧若拙
12235820 2014-11-19 13:07:33 Accepted 1285
15MS 460K 4004 B
C 巧若拙
代码如下:
算法1:
#include<stdio.h>
#include<stdlib.h>
#define MAXN 502 //最大顶点数量
int map[MAXN][MAXN] = {0};
void TopoLogicalSort(int n);
int main()
{
int i, j, m, n, u, v;
while(scanf("%d%d", &n, &m) != EOF)
{
for (i=0; i<MAXN; i++)
for (j=0; j<MAXN; j++)
map[i][j] = 0;
for (i=0; i<m; i++)
{
scanf("%d%d", &u, &v);
if (map[u][v] == 0) //数据可能会重复
{
map[u][v] = 1;
map[0][v]++; //存储顶点v的入度
}
}
TopoLogicalSort(n);
}
return 0;
}
void TopoLogicalSort(int n)
{
int i, j, top;
int topo[MAXN] = {0};
for (top=0; top<n; top++)//总共有n个顶点,搜索n次
{
for (i=1; i<=n; i++)//寻找入度为0的序号最小的顶点
{
if (map[0][i] == 0)
{
map[0][i] = -1;
break;
}
}
topo[top] = i;
for (j=1; j<=n; j++) //弧尾i对应弧头j入度减1
{
if (map[i][j] == 1)
map[0][j]--;
}
}
for (i=0; i<top-1; i++)
{
printf("%d ", topo[i]);
}
printf("%d\n", topo[top-1]);
}
算法2:
#include<stdio.h>
#include<stdlib.h>
#define MAXN 510 //最大变量(顶点)数量
typedef int VertexType; //顶点类型由用户自定义
typedef int EdgeType; //边上的权值类型由用户自定义
typedef struct EdgeNode{ //边表结点
int adjvex; //邻接点域,存储该顶点对应的下标
// EdgeType weight; //权值,对于非网图可以不需要
struct EdgeNode *next; //链域,指向下一个邻接点
} EdgeNode;
typedef struct VertexNode{ //顶点表结点
VertexType data; //顶点域,存储顶点信息
int in; //存储顶点入度的数量
EdgeNode *firstEdge; //边表头指针
} VertexNode;
void CreateGraph(VertexNode *GL, int n, int m);//把顶点和边信息读入到表示图的邻接表中
int InsertStack(int vec[], int x, int n);//折半插入,递减排序
void TopoLogicalSort_DFS(VertexNode *GL, int n);
int main()
{
int i, m, n;
VertexNode GL[MAXN];
while(scanf("%d%d", &n, &m) != EOF)
{
CreateGraph(GL, n, m);//把顶点和边信息读入到表示图的邻接表中
TopoLogicalSort_DFS(GL, n);
}
return 0;
}
void CreateGraph(VertexNode *GL, int n, int m)//把顶点和边信息读入到表示图的邻接表中
{
int i, u, v;
EdgeNode *e;
for (i=1; i<=n; i++)//初始化图
{
GL[i].data = i;
GL[i].in = 0;
GL[i].firstEdge = NULL;
}
for (i=0; i<m; i++)
{
e = (EdgeNode*)malloc(sizeof(EdgeNode)); //采用头插法插入边表结点
if (!e)
{
puts("Error");
exit(1);
}
scanf("%d%d", &u, &v);
e->next = GL[u].firstEdge;
GL[u].firstEdge = e;
e->adjvex = v;
GL[v].in++;
}
}
int InsertStack(int vec[], int x, int n)//折半插入,递减排序
{
int low = 0, high = n - 1, mid, j;
while(low <= high) //折半查找插入位置
{
mid = (low + high)/2;
if(vec[mid] < x)
{
high = mid -1;
}
else
{
low = mid + 1;
}
}
//进行插入操作
for(j=++n; j>low; j--)
{
vec[j] = vec[j-1];
}
vec[low] = x;
return n;
}
void TopoLogicalSort_DFS(VertexNode *GL, int n)
{
int i, u, v, top = 0;
int count = 0;
EdgeNode *e;
int topo[MAXN], Stack[MAXN];//有序栈(或优先队列)
for (i=1; i<=n; i++)//将入度为0的顶点按序号大小逆序入栈
{
if (GL[i].in == 0)
{
top = InsertStack(Stack, i, top);
}
}
while (top > 0)//采用深度优先搜索获取拓扑序列
{
u = Stack[--top];
topo[count++] = u;
for (e=GL[u].firstEdge; e!=NULL; e=e->next)//将u的邻接点入度减1,并将入度为0的顶点入栈
{
v = e->adjvex;
if (--GL[v].in == 0)
{
top = InsertStack(Stack, v, top);
}
}
}
for (i=0; i<count-1; i++)
{
printf("%d ", topo[i]);
}
printf("%d\n", topo[count-1]);
}