九度OJ 1100:最短路径 (最短路径)

时间限制:1 秒

内存限制:32 兆

特殊判题:

提交:4185

解决:619

题目描述:

N个城市,标号从0到N-1,M条道路,第K条道路(K从0开始)的长度为2^K,求编号为0的城市到其他城市的最短距离

输入:

第一行两个正整数N(2<=N<=100)M(M<=500),表示有N个城市,M条道路
接下来M行两个整数,表示相连的两个城市的编号

输出:

N-1行,表示0号城市到其他城市的最短路,如果无法到达,输出-1,数值太大的以MOD 100000 的结果输出。

样例输入:
4 4
1 2
2 3
1 3
0 1
样例输出:
8
9
11
来源:
2010年上海交通大学计算机研究生机试真题

思路:

此题的路径长度很特殊,不能用普通的数值来表示。可以用数组或字符串的方式来表示路径长度,同时定义相应的大整数运算。

但由于路径长度的特殊性,更简单的办法是求最小生成树,求的过程中顺便求得最短路径。

我开始用的第一种方法,需要考虑的细节较多,出错了几次,后来用第二种方法AC的。


代码:

#include <stdio.h>
#include <stdlib.h>
 
int rank[100] ;//记录每个树的深度
int pre[100];//记录每个点的父节点
int d[100][100];//记录各对间的距离
 
void initSet(int n)//初始化,将每个点的父节点设为自己,深度为1
{
    int i;
    for(i=0; i<n; i++)
    {
        rank[i] = 1;
        pre[i] = i;
        d[i][i] = 0;
    }
}
 
int findSet(int x)//找到每个点的父节点,并将这个点的父节点设置为数的根节点
{
    if(x != pre[x])
        pre[x] = findSet(pre[x]);
    return pre[x];
}
 
void unionSet(int a, int b)//合并树,
{
    int x = findSet(a);
    int y = findSet(b);
    if(x == y)//如果两个节点的父节点(树的根节点)是同一个,无需合并,直接跳过
        return;
    if(rank[x] >= rank[y])
    {
        rank[x] += rank[y];
        pre[y] = x;
    }
    else
    {
        rank[y] += rank[y];
        pre[x] = y;
    }//不是同一个树的的节点,小树合并到大树
}
 
int mod(int a, int b)//取模
{
    int ret = 1;
    while(b--)
        ret = (ret*a)%100000;
    return ret;
}
 
int main()
{
    int n, m, dist;
    int x, y, a, b;
    int i, j, k;
    while(scanf("%d%d", &n, &m)!=EOF)
    {
        initSet(n);
        for(i=0; i<m; i++)
        {
            scanf("%d%d", &x, &y);
            a = findSet(x);
            b = findSet(y);
            if(a == b)//二者已在同一个连通分量,距离定是最小了
                continue;
            dist = mod(2, i);//取模
            for(j=0; j<n; j++)//更新两个连通分量的各对经过中间对的距离
            {
                if(a != findSet(j))
                    continue;
                for(k=0; k<n; k++)
                {
                    if(b != findSet(k))
                        continue;
                    d[j][k] = d[k][j] = (d[j][x]+dist+d[y][k])%100000;
                }
            }
            unionSet(x, y);
        }
        x = findSet(0);
        for(i=1; i<n; i++)
            if(findSet(i) != x)
                printf("-1\n");
            else
                printf("%d\n", d[0][i]);
    }
}  
/**************************************************************
    Problem: 1100
    User: liangrx06
    Language: C
    Result: Accepted
    Time:10 ms
    Memory:952 kb
****************************************************************/



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#define MAXVEX 30 #define MAXCOST 1000 void prim(int c[MAXVEX][MAXVEX],int n) /*己知图的顶点为{1,2,...,n},c[i][j]和c[j][i]为边(i,j)的权,打印最小生成树 的每条边*/ { int i,j,k,min,lowcost[MAXVEX],closest[MAXVEX];; for (i=2;i<=n;i++) /*从顶点1开始*/ { lowcost[i]=c[1][i]; closest[i]=1; } closest[1]=0; for (i=2;i<=n;i++) /*从U之外求离U中某一顶点最近的顶点*/ { min=MAXCOST; j=1;k=i; while (j<=n) { if (lowcost[j]<min && closest[j]!=0) { min=lowcost[j]; k=j; } j++; } printf("(%d,%d) ",closest[k],k); /*打印边*/ closest[k]=0; /*k加入到U中*/ for (j=2;j<=n;j++) if (closest[j]!=0 && c[k][j]<lowcost[j]) { lowcost[j]=c[k][j]; closest[j]=k; } } } main() { int n=7,i,j,mx[MAXVEX][MAXVEX]; for (i=0;i<=n;i++) for (j=0;j<=n;j++) mx[i][j]=MAXCOST; mx[1][2]=50; mx[1][3]=60; mx[2][4]=65; mx[2][5]=40; mx[3][4]=52; mx[3][7]=45; mx[4][5]=50; mx[5][6]=70; mx[4][6]=30; mx[4][7]=42; printf("最小生成树边集:\n "); prim(mx,n); } 数据结构导学上的, /*克鲁斯卡尔算法构造最小生成树*/ #define MAXEDGE 30 /*MAXEDGE为最大的边数*/ struct edges /*边集类型,存储一条边的起始顶点bv、终止顶点tv和权w*/ { int bv,tv,w; }; typedef struct edges edgeset[MAXEDGE]; int seeks(int set[],int v) { int i=v; while (set[i]>0) i=set[i]; return(i); } kruskal(edgeset ge,int n,int e) /*ge表示的图是按权值从小到大排列的*/ { int set[MAXEDGE],v1,v2,i,j; for (i=1;i<=n;i++) set[i]=0; /*给set中的每个元素赋初值*/ i=1; /*i表示待获取的生成树中的边数,初值为1*/ j=1; /*j表示ge中的下标,初值为1*/ while (j<n && i<=e) /*按边权递增顺序,逐边检查该边是否应加入到生成树中*/ { v1=seeks(set,ge[i].bv); /*确定顶点v所在的连通集*/ v2=seeks(set,ge[i].tv); if (v1!=v2) /*当v1,v2不在同一顶点集合,确定该边应当选入生成树*/ { printf("(%d,%d) ",ge[i].bv,ge[i].tv); set[v1]=v2; j++; } i++; } } main() { int n=7,e=10; edgeset mx; mx[1].bv=4;mx[1].tv=6;mx[1].w=30; mx[2].bv=2;mx[2].tv=5;mx[2].w=40; mx[3].bv=4;mx[3].tv=7;mx[3].w=42; mx[4].bv=3;mx[4].tv=7;mx[4].w=45; mx[5].bv=1;mx[5].tv=2;mx[5].w=50; mx[6].bv=4;mx[6].tv=5;mx[6].w=50; mx[7].bv=3;mx[7].tv=4;mx[7].w=52; mx[8].bv=1;mx[8].tv=3;mx[8].w=60; mx[9].bv=2;mx[9].tv=4;mx[9].w=65; mx[10].bv=5;mx[10].tv=6;mx[10].w=70; printf("最小生成树边集:\n "); kruskal(mx,n,e); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值