最小生成树在城市建设道路中的应用---图论及其应用论文

最小生成树在城市建设道路中的应用

摘 要
图论和我们的生活息息相关,现实生活中随处可见图论的实际应用。图分为有向图和无向图,本文主要讨论无向图。本文的目的是利用图论的相关知识来解决海平面上升后部分城市重新建设道路中的问题。本文中,解决该问题应用的是图论中的最小生成树中的prim(普里姆)算法。
本文通过构造连通图进而解决问题,图中的节点是海平面上升后所有幸存的城市ID(为简单起见,城市从1到n),已连接的边代表可选择的建设道路,边上的权值代表重修该道路的费用,无需重修道路的权值为0。在c语言上,通过输入可连通道路的节点以及权值,用prim算法来得到最小生成树,实现各个幸存城市能连通的情况下,政府花费最小,输出政府需要的花费。
关键词:最小生成树;prim算法;连通图;城市建设道路

一,连通图
在图论中,连通图基于连通的概念。在一个无向图G中,若从顶点到顶点有路径相连(当然从到也一定有路径),则称和是连通的。如果G是有向图,那么连接和的路径中所有的边都必须同向。如果图中任意两点都是连通的,那么图被称作连通图。图的连通性是图的基本性质。
(1)严格定义
对一个图G= (V,E)中的两点和,若存在交替的顶点和边的序列(在有向图中要求有向边属于E),则两点和是连通的。是一条到的连通路径,和分别是起点和终点。当时,被称为回路。如果通路中的边两两不同,则是一条简单通路,否则为一条复杂通路。如果图G中每两点间皆连通,则G是连通图。
(2)相关概念
连通分量:无向图G的一个极大连通子图称为G的一个连通分量(或连通分支)。连通图只有一个连通分量,即其自身;非连通的无向图有多个连通分量。
初级通路:通路中所有的顶点互不相同。初级通路必为简单通路,但反之不真。
(3)性质
一个无向图G= (V,E)是连通的,那么边的数目大于等于顶点的数目减一:,而反之不成立。
没有回路的无向图是连通的当且仅当它是树,即等价于:|E|=|V|-1。
二,最小生成树
(1)树
在图论中,树是一种无向图,其中任意两个顶点间存在唯一一条路径。或者说,只要没有回路的连通图就是树。树图广泛应用于计算机科学的数据结构中,比如二叉查找树,堆,Trie树以及数据压缩中的霍夫曼树等等。
在这里插入图片描述

(2)邻接矩阵
邻接矩阵是表示顶点之间相邻关系的矩阵。设G=(V,E)是一个图,其中V={v1,v2,…,vn}。G的邻接矩阵是一个具有下列性质的n阶方阵:
①对无向图而言,邻接矩阵一定是对称的,而且对角线一定为零(在此仅讨论无向简单图),有向图则不一定如此。
②在无向图中,任一顶点i的度为第i列所有元素的和,在有向图中顶点i的出度为第i行所有元素的和,而入度为第i列所有元素的和。
(3)最小生成树
最小生成树是一副连通加权无向图中一棵权值最小的生成树。
在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即 ),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即 )且 (V, T) 为树,使得
w(T)=的 w(T) 最小,则此 T 为 G 的最小生成树。
最小生成树其实是最小权重生成树的简称。
一个连通图可能有多个生成树。当图中的边具有权值时,总会有一个生成树的边的权值之和小于或者等于其它生成树的边的权值之和。广义上而言,对于非连通无向图来说,它的每一连通分量同样有最小生成树,它们的并被称为最小生成森林。以有线电视电缆的架设为例,若只能沿着街道布线,则以街道为边,而路口为顶点,其中必然有一最小生成树能使布线成本最低。
三,prim算法
Prim(普里姆)算法,可在加权连通图里搜索最小生成树。即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。
(1)算法描述
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew= {x},其中x为集合V中的任一节点(起始点),Enew= {},为空;
3).重复下列操作,直到Vnew= V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
(2)图例描述
1、此为原始的加权连通图。每条边一侧的数字代表其权值。
在这里插入图片描述
2、可以选择任意点为起始点,比如从顶点D开始出发。顶点A、B、E和F通过单条边与D相连。A是距离D最近的顶点,因此将顶点A放入Vnew中,即Vnew={D},Enew为空。然后将Vnew中的顶点(用圈)及即将被选中的距离Vnew最近的边AD(用箭头)表示出来。并将顶点A放入Vnew中,边DA放入Enew中。
在这里插入图片描述

3、更新Vnew={D,A},Enew={AD}。下一个顶点为距离Vnew最近的顶点。B距D为9,距A为7,E为15,F为6。因此,F是距离Vnew最近的顶点。将Vnew中的顶点(用圈)及即将被选中的距离Vnew最近的边DF(用箭头)表示出来。并将顶点F放入Vnew中,边DF放入Enew中。

在这里插入图片描述
4、更新Vnew={D,A,F},Enew={AD,DF}。算法继续重复上面的步骤。B是距离Vnew最近的顶点。将Vnew中的顶点(用圈)及即将被选中的距离Vnew最近的边DF(用箭头)表示出来。并将顶点B放入Vnew中,边AB放入Enew中。

在这里插入图片描述

5、更新Vnew={D,A,F,B},Enew={AD,DF,AB}。在当前情况下,可以在C、E与G间进行选择。C距B为8,E距B为7,G距F为11。因此E是距离Vnew最近的顶点。将Vnew中的顶点(用圈)及即将被选中的距离Vnew最近的边BE(用箭头)表示出来。并将顶点E放入Vnew中,边BE放入Enew中。
在这里插入图片描述

6、更新Vnew={D,A,F,B,E},Enew={AD,DF,AB,BE}。这里,可供选择的顶点只有C和G。C距E为5,G距E为9,故选取C。将Vnew中的顶点(用圈)及即将被选中的距离Vnew最近的边BE(用箭头)表示出来。并将顶点C放入Vnew中,边CE放入Enew中。)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200107160155287.png在这里插入图片描述
7、更新Vnew={D,A,F,B,E,C},Enew={AD,DF,AB,BE,CE}。顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近。故将Vnew中的顶点(用圈)及即将被选中的距离Vnew最近的边EG(用箭头)表示出来。并将顶点G放入Vnew中,边EG放入Enew中。
在这里插入图片描述

8、现在,更新Vnew={D,A,F,B,E,C,G},Enew={AD,DF,AB,BE,CE,EG}。所有顶点均已被选取,图中绿色加粗部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为5+6+7+7+5+9=39。
在这里插入图片描述
四,实际问题及解决
(1)实际问题描述
问题背景:根据纽约时报的报道,一项最新研究显示,到2050年,海平面上升对人类的影响将是之前预计的三倍,这可能会几乎抹掉世界上一些最重要的沿海城市。新的研究表明,到本世纪中叶,约有1.5亿人将生活在海平面以下的陆地上。越南南部可能会完全消失。
有专家预测,到2100年,由于海平面上升,大多数城市消失了。尽管一些幸存的城市仍与其他城市保持联系,但其中大多数却与世隔绝。希望修建一些道路以再次连接所有这些城市,但不想要花太多钱。
输入:
每个测试用例均以三个整数开头:n,m和k。 n(3 <= n <= 500)代表存活的城市数量,m(0 <= m <= 25000)代表可以选择连接城市的道路数量,而k(0 <= k <= 100)代表仍处于连接状态的城市数。为了简单起见,城市从1到n进行标记。然后跟随m行,每行包含三个整数p,q和c(0 <= c <= 1000),这意味着需要c来连接p和q。然后跟随k行,每行以整数t(2 <= t <= n)开头,代表该连接城市的数量。然后,跟随t个整数代表这些城市的ID。
输出:
对于每种情况,输出您所需的最少花费,如果不可能,则仅输出-1。
以下是该问题的C语言代码:
注意:(1)该问题要考虑到输入的t行数据,该t行数据为本来就已经连接好的城市。(2)以下的Vnew和以上介绍prim算法的Vnew是一个含义(作为被选中节点城市的集合,同时也是访问过的节点的集合)。
注意:

#include<stdio.h>
#include<string.h>
#define N 505
#define INF 0x7ffffff
int g[N][N];
int low[N],vis[N];
int n;
int prim()
{
    int pos,res=0,ci=0;
    memset(vis,0,sizeof(vis));
    vis[1]=1;//将ID为1的城市作为该图的第一个起始点
    pos=1;
    for(int i=1; i<=n; i++)
        if(i!=pos)
            low[i]=g[pos][i];
    printf("第%d个被任意选中的节点是ID为%d的城市\n被选中后,图的权值更新情况如下:\n",++ci,pos);
    for(int i=1; i<=n; i++)
        if(i!=pos)
            printf("ID为%d的城市若想连接Vnew里面所有的城市,需要的花费分别为%d\n",i,low[i]);
    for(int i=1; i<n; i++)//一共n个城市,还需选n-1个(上面已经选了ID为1的城市为起始点)
    {
        int minn=INF;
        pos=-1;
        for(int j=1; j<=n; j++)
            if(!vis[j] && minn>low[j])
            {
                minn=low[j];//实时更新minn(边上对应的最小权值的值)
                pos=j;//实时更新即将被选中的城市ID
            }
        if(pos==-1)
            return -1; //注意这里,接下来无法进行任意两个城市之间的修路,即此项任务无法完成
        res+=minn;//每次循环更新权值和
        vis[pos]=1;//标记该次循环被选中的城市ID已经被访问过(下次无需访问该城市ID)
        //printf("**%d %d\n",pos,minn);
        printf("\n第%d个被选中的节点是ID为%d的城市\n被选中后,图的权值更新情况如下:\n",++ci,pos);
        for(int j=1; j<=n; j++)
            if(!vis[j] && low[j]>g[pos][j])
                low[j]=g[pos][j];//实时更新各个城市到已访问过的城市的最小距离
        for(int i=1; i<=n; i++)
            if(!vis[i])
                printf("ID为%d的城市若想连接Vnew里面所有的城市,需要的花费分别为%d\n",i,low[i]);
    }
    return res;
}
int main(void)
{
    int t,m,k;
    int u,v,d;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0; i<=n; i++)
            for(int j=0; j<=n; j++)
                g[i][j]=INF;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&u,&v,&d);
            if(g[u][v]>d)    //notice
                g[u][v]=g[v][u]=d;//按照案例构造具有可选道路的图,权值等等
        }
        for(int i=0; i<k; i++)
        {
            scanf("%d",&d);
            scanf("%d",&u);
            d--;
            while(d--)
            {
                scanf("%d",&v);
                g[u][v]=g[v][u]=0;//注意将幸存下来的而且本来就有道路的道路权值标记为0(因为无需花钱修该条道路了)
                u=v;
            }
        }
        printf("%d\n",prim());
    }
    return 0;
}

以下是该代码运行的结果:

在这里插入图片描述
五,结论
通过prim(普里姆)算法为核心编写的c语言程序,可以轻易的得出最后需要的总花费,而且可以保证花费是最小值。这将会很大程度上节约花费成本,也是对现实中的实际问题的一次尝试解决。该算法应用广泛,应该得到更大的改善和推广,以便应用到更多更大的问题。

参考文献
【1】《C语言程序设计》(第三版),谭浩强,清华大学出版社。 
【2】《图论及其应用》张清华主编,陈六新,李永红副主编,清华大学出版社。
【3】《数据结构》(C语言版),吴伟民,清华大学出版社。  
【4】《算法竞赛入门经典》(第二版),刘汝佳,清华大学出版社。

  • 12
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值