标题:杭州电子科技大学 Online Judge 之 “确定比赛名次(ID1285)”解题报告
只看楼主
巧若拙
Rank: 4
来 自:宁波余姚
等 级:业余侠客
威 望:1
帖 子:159
专家分:273
注 册:2014-8-24
结帖率:46.15%
已结贴  问题点数:3 回复次数:6 
杭州电子科技大学 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]);
}
搜索更多相关主题的帖子: Online 委员会 杭州 大学 电子 
2014-11-19 14:38
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
得分:3 
交流下编码心得由于问题规模不大,所以也没进行优化。
程序代码:
#include <stdio.h>
int main()
{
    char a[512][512];
    int b[512], n, m, i, j, k;
    for(; scanf("%d%d", &n, &m) != EOF; puts(""))
    {
        for(i = 1; i <= n; b[i++] = 0)
        for(j = 1; j <= n; a[i][j++] = 0);
        for(i = 0; i++ < m; scanf("%d%d", &j, &k), b[k] += a[j][k] ? 0 : (a[j][k] = 1));
        for(i = 0; i < n; printf(i++ ? " %d" : "%d", j))
        {
            for(j = 1; j <= n && b[j]; j++);
            for(b[j] = -1, k = 0; ++k <= n; b[k] -= a[j][k]);
        }
    }
    return 0;
}

重剑无锋,大巧不工
2014-11-19 19:17
巧若拙
Rank: 4
来 自:宁波余姚
等 级:业余侠客
威 望:1
帖 子:159
专家分:273
注 册:2014-8-24
得分:0 
beyondyf真是太牛了!你的算法思想和我的算法1是一样的,但代码却比我要短很多。我尝试着解读了一下,并斗胆把puts("")改成了printf("\n"),顺便问一句,你为什么要用puts("")?是不是他的效率更高?
我是刚学算法,还只会照猫画虎,很多算法思想用的不是很熟练,还请多多指导,谢谢!


#include<stdio.h>
#include<stdlib.h>

int main()
{
    char a[512][512];
    int b[512], n, m, i, j, k;
   
    for (; scanf("%d%d", &n, &m) != EOF; printf("\n"))
    {
        for (i = 1; i <= n; b[i++] = 0)//邻接矩阵初始化
            for (j = 1; j <= n; a[i][j++] = 0) ;
            
        for (i = 0; i++ < m; scanf("%d%d", &j, &k), b[k] += a[j][k] ? 0 : (a[j][k] = 1)) ; //输入图信息
         
        for (i = 0; i < n; printf(i++ ? " %d" : "%d", j))//很巧妙的输出格式啊,呵呵
        {
            for (j = 1; j <= n && b[j]; j++) ;//按序号大小寻找入度为0的顶点j
            for (b[j] = -1, k = 0; ++k <= n; b[k] -= a[j][k]) ;//弧尾j对应弧头k入度减1
        }
    }
    return 0;
}
2014-11-19 20:24
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
得分:0 
过奖了。用puts("")只为少打几个字符看着清爽,效率上略好(不需要解析格式字符串),但这可以忽略。用你习惯的用法就好。

看你常发算法心得,但就是帖子里不带分。好容易这次有3分,一定要抢来。

重剑无锋,大巧不工
2014-11-19 20:37
巧若拙
Rank: 4
来 自:宁波余姚
等 级:业余侠客
威 望:1
帖 子:159
专家分:273
注 册:2014-8-24
得分:0 
呵呵,我刚来论坛,分数不多,不知道怎么用,下次多发点
2014-11-19 20:38
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
得分:0 
呵呵抢分是玩笑话,别介意。其实是你之前的帖子看起来更像是个人笔记,也不知道该回些什么好。写的挺好。

重剑无锋,大巧不工
2014-11-19 21:10
巧若拙
Rank: 4
来 自:宁波余姚
等 级:业余侠客
威 望:1
帖 子:159
专家分:273
注 册:2014-8-24
得分:0 
我贴的东西确实是学习笔记,比较稚嫩。这是我第一次较为认真地学习数据结构和算法,特地买了好几本书一起看(《大话数据结构》,《啊哈算法》,《数据结构和算法分析--C语言描述》,还有《算法导论》,但里面的数学证明有些看不懂,跳过了),相互比较,写的代码并没有照搬书上的,有一点点自己的体会,虽然简单测试能通过,但不敢保证是对的,所以贴出来希望能得到高手的指点。
顺便汇报一下学习进度,前面链表,树什么的感觉学起来挺快的,现在图论了,有些吃力,看来还得耗上一段时间,我在网上做一些OJ题目,也是为了熟练运用算法,但感觉做起来有些吃力,而且代码都很臃肿什么的,呵呵。请您提出宝贵意见。
2014-11-20 06:49



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-438860-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.494896 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved