浙大数据结构第八周之08-图7 公路村村通

题目详情:

现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。

输入格式:

输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。

输出格式:

输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。

输入样例:

6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3

输出样例:

12

主要思路:

先补一下邻接表建图

邻接表的处理方法:

(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。

例如,下图就是一个无向图的邻接表的结构:

 从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,

data是数据域,存储顶点的信息,

firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点

边表结点由adjvex和next两个域组成。

adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,(可以通过此下标在一维顶点数组中查询到这个顶点的信息)

next则存储指向边表中下一个结点的指针。


数据结构一:边:

typedef struct ENode *PtrToENode;
struct ENode{Vertex V1, V2;      /* 有向边<V1, V2> */WeightType Weight;  /* 权重 */
};
typedef PtrToENode Edge;

数据结构二:邻接点

/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode; 
struct AdjVNode{Vertex AdjV;        /* 邻接点下标 */WeightType Weight;  /* 边权重 */PtrToAdjVNode Next;    /* 指向下一个邻接点的指针 */
};

 数据结构三:顶点表头节点

/* 顶点表头结点的定义 */
typedef struct Vnode{PtrToAdjVNode FirstEdge;   /* 指向边表的第一个结点,即此顶点的第一个邻接点 */DataType Data;            /* 存顶点的数据 *//* 注意:很多情况下,顶点无数据,此时Data可以不用出现 */
} AdjList[MaxVertexNum];    /* AdjList是邻接表类型 */

数据结构四:图节点

/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{  int Nv;     /* 顶点数 */int Ne;     /* 边数   */AdjList G;  /* 邻接表 */
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */

初始化有顶点没有边的空图:

LGraph CreateGraph( int VertexNum )
{ /* 初始化一个有VertexNum个顶点但没有边的图 */Vertex V;LGraph Graph;Graph = (LGraph)malloc( sizeof(struct GNode) ); /* 建立图 */Graph->Nv = VertexNum;Graph->Ne = 0;/* 初始化邻接表头指针 *//* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */for (V=0; V<Graph->Nv; V++)Graph->G[V].FirstEdge = NULL;   //将每个顶点的邻接链表的头结点指针设置为 NULL。return Graph; 
}

插入边(插入的时候是头插法)

void InsertEdge( LGraph Graph, Edge E )
{    /* 插入边 <V1, V2> *//* 为V2建立新的邻接点 */PtrToAdjVNode NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));NewNode->AdjV = E->V2;NewNode->Weight = E->Weight;/* 将V2插入V1的表头,插入的边表示从v1指向v2 */NewNode->Next = Graph->G[E->V1].FirstEdge;Graph->G[E->V1].FirstEdge = NewNode;/* 若是无向图,还要插入边 <V2, V1> *//* 为V1建立新的邻接点 */NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));NewNode->AdjV = E->V1;NewNode->Weight = E->Weight;/* 将V1插入V2的表头 */NewNode->Next = Graph->G[E->V2].FirstEdge;Graph->G[E->V2].FirstEdge = NewNode;
}

建图 

LGraph BuildGraph()
{LGraph Graph;Edge E;Vertex V;int Nv, i;scanf("%d", &Nv);   /* 读入顶点个数 */Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */ scanf("%d", &(Graph->Ne));   /* 读入边数 */if ( Graph->Ne != 0 ) { /* 如果有边 */ E = (Edge)malloc( sizeof(struct ENode) ); /* 建立边结点 */ /* 读入边,格式为"起点 终点 权重",插入邻接矩阵 */for (i=0; i<Graph->Ne; i++) {scanf("%d %d %d", &E->V1, &E->V2, &E->Weight); /* 注意:如果权重不是整型,Weight的读入格式要改 */InsertEdge( Graph, E );}} /* 如果顶点有数据的话,读入数据 */for (V=0; V<Graph->Nv; V++) scanf(" %c", &(Graph->G[V].Data));return Graph;
}

然后是Prim算法:

/* 邻接矩阵存储 - Prim最小生成树算法 */Vertex FindMinDist( MGraph Graph, WeightType dist[] )
{ /* 返回未被收录顶点中dist最小者 */Vertex MinV, V; WeightType MinDist = INFINITY;for (V=0; V<Graph->Nv; V++) {if ( dist[V]!=0 && dist[V]<MinDist) {/* 若V未被收录,且dist[V]更小 */MinDist = dist[V]; /* 更新最小距离 */MinV = V; /* 更新对应顶点 */}}if (MinDist < INFINITY) /* 若找到最小dist */return MinV; /* 返回对应的顶点下标 */else return ERROR;  /* 若这样的顶点不存在,返回-1作为标记 */
}int Prim( MGraph Graph, LGraph MST )
{ /* 将最小生成树保存为邻接表存储的图MST,返回最小权重和 */WeightType dist[MaxVertexNum], TotalWeight;Vertex parent[MaxVertexNum], V, W;int VCount;Edge E;/* 初始化。默认初始点下标是0 */for (V=0; V<Graph->Nv; V++) {/* 这里假设若V到W没有直接的边,则Graph->G[V][W]定义为INFINITY */dist[V] = Graph->G[0][V];parent[V] = 0; /* 暂且定义所有顶点的父结点都是初始点0 */ }TotalWeight = 0; /* 初始化权重和     */VCount = 0;      /* 初始化收录的顶点数 *//* 创建包含所有顶点但没有边的图。注意用邻接表版本 */MST = CreateGraph(Graph->Nv);E = (Edge)malloc( sizeof(struct ENode) ); /* 建立空的边结点 *//* 将初始点0收录进MST */dist[0] = 0;VCount ++;parent[0] = -1; /* 当前树根是0 */while (1) {V = FindMinDist( Graph, dist );/* V = 未被收录顶点中dist最小者 */if ( V==ERROR ) /* 若这样的V不存在 */break;   /* 算法结束 *//* 将V及相应的边<parent[V], V>收录进MST */E->V1 = parent[V];E->V2 = V;E->Weight = dist[V];InsertEdge( MST, E );TotalWeight += dist[V];dist[V] = 0;VCount++;for( W=0; W<Graph->Nv; W++ ) /* 对图中的每个顶点W */if ( dist[W]!=0 && Graph->G[V][W]<INFINITY ) {/* 若W是V的邻接点并且未被收录 */if ( Graph->G[V][W] < dist[W] ) {/* 若收录V使得dist[W]变小 */dist[W] = Graph->G[V][W]; /* 更新dist[W] */parent[W] = V; /* 更新树 */}}} /* while结束*/if ( VCount < Graph->Nv ) /* MST中收的顶点不到|V|个 */TotalWeight = ERROR;return TotalWeight;   /* 算法执行完毕,返回最小权重和或错误标记 */
}

其实本题可以只用邻接矩阵构建的图(或邻接表构建的图)也能解决,因为本题只要求MST的权值,并没有考察更多MST的性质,不过就当巩固所学吧 

代码实现:

#include <stdio.h>
#include <stdlib.h>
#define MAX_NODE_NUMS 1005
#define INFINITY 100000
#define TRUE 1
#define FALSE 0
#define NONE -1
#define ROOT 1
typedef int bool;
/*ListGraph的数据结构*/
/*边*/
typedef struct ENode ENode;
typedef ENode* PToEdgeNode;
struct ENode {int Start, End, Weight;
};
/*邻接点*/
typedef struct AdjVNode AdjVNode;
typedef AdjVNode* PToAdjVNode;
struct AdjVNode {int VertexIndex, Weight;PToAdjVNode Next;
};
/*头结点*/
typedef struct HeadNode HeadNode;
struct HeadNode {int Weight;PToAdjVNode FirstEdge;
};
/*图节点*/
typedef struct ListGraphNode ListGraphNode;
typedef ListGraphNode* ListGraph;
struct ListGraphNode {int EdgeNums, VertexNums;HeadNode AdjList[MAX_NODE_NUMS];
};
/*建一个空图*/
ListGraph CreateEmptyListGraph(int vertexNums) {ListGraph LGraph = (ListGraph)malloc(sizeof(ListGraphNode));LGraph->EdgeNums = 0; LGraph->VertexNums = vertexNums;for(int i = 0; i <= vertexNums; i++) {LGraph->AdjList[i].FirstEdge = NULL;}return LGraph;
}
/*插入边*/
void InsertEdgeInLGraph(ListGraph LGraph, PToEdgeNode edge) {/*插入<start, end>的边*/PToAdjVNode newVertex = (PToAdjVNode)malloc(sizeof(AdjVNode));newVertex->VertexIndex = edge->End;newVertex->Weight = edge->Weight;newVertex->Next = LGraph->AdjList[edge->Start].FirstEdge;LGraph->AdjList[edge->Start].FirstEdge = newVertex;/*插入<end,start>的边,这是因为无向图,如果是有向图可以省略*/newVertex = (PToAdjVNode)malloc(sizeof(AdjVNode));newVertex->VertexIndex = edge->Start;newVertex->Weight = edge->Weight;newVertex->Next = LGraph->AdjList[edge->End].FirstEdge;LGraph->AdjList[edge->End].FirstEdge = newVertex;return;
}
ListGraph BuildListGraph(int vertexNums, int edgeNums) {ListGraph LGraph = CreateEmptyListGraph(vertexNums);for(int i = 0; i < edgeNums; i++) {PToEdgeNode newEdge = (PToEdgeNode)malloc(sizeof(ENode));scanf("%d %d %d", &(newEdge->Start), &(newEdge->End), &(newEdge->Weight));InsertEdgeInLGraph(LGraph, newEdge);free(newEdge);}return LGraph;
}/*MatrixGraph的数据结构*/
typedef struct MatrixGraphNode MatrixGraphNode;
typedef MatrixGraphNode* MatrixGraph;
struct MatrixGraphNode {int VertexNums, EdgeNums;int Weight[MAX_NODE_NUMS][MAX_NODE_NUMS];
};
MatrixGraph CreateEmptyMatrixGraph(int vertexNums) {MatrixGraph MGraph = (MatrixGraph)malloc(sizeof(MatrixGraphNode));MGraph->VertexNums = vertexNums;MGraph->EdgeNums = 0;for(int i = 0; i <= vertexNums; i++) {for(int j = 0; j <= vertexNums; j++) {MGraph->Weight[i][j] = INFINITY;}}return MGraph;
}
void InsertEdgeInMGraph(int start, int end, int weight, MatrixGraph MGraph) {MGraph->Weight[start][end] = weight;MGraph->Weight[end][start] = weight;return;
}
MatrixGraph BuildMGraph(int vertexNums, int edgeNums) {MatrixGraph MGraph = CreateEmptyMatrixGraph(vertexNums);MGraph->EdgeNums = edgeNums;for(int i = 0; i < edgeNums; i++) {int start, end, weight;scanf("%d %d %d", &start, &end, &weight);InsertEdgeInMGraph(start, end, weight, MGraph);}return MGraph;
}
/*Prim算法*/
/*在剩余节点中找到与最小生成树权值最小的边*/
int FindMinDis(MatrixGraph MGraph, const int dis[]) {int minV = NONE;int minDist = INFINITY;for(int i = 1; i <= MGraph->VertexNums; i++) {if(dis[i] != FALSE && dis[i] < minDist) { //dist其实兼顾了Dijkstra中vis数组的作用minDist = dis[i];minV = i;}}return minV;
}
int Prim(MatrixGraph MGraph) {int dis[MAX_NODE_NUMS];     //dis[i]表示节点i到最小生成树的距离int parent[MAX_NODE_NUMS];int totalWeight = 0;int Vcount = 0;/*初始化dis和path数组,默认是从下标1开始,因为顶点从下标1开始*/for(int i = 1; i <= MGraph->VertexNums; i++) {dis[i] = MGraph->Weight[ROOT][i];  //由初始化可以看出,如果ROOT(定这个ROOT的原因是因为最小生成树只有一个根节点)~i两个节点之间有边,就初始化为权值,否则就初始化为INFINITYparent[i] = ROOT;    //假设所有顶点的上一级顶点都是ROOT}/*开始建立最小生成树*/ListGraph MST = CreateEmptyListGraph(MGraph->VertexNums);dis[ROOT] = 0;    //将顶点1作为最小生成树的根节点Vcount++;parent[ROOT] = NONE;while(TRUE) {int minV = FindMinDis(MGraph, dis);if(minV == NONE) break;/*将minV加入到最小生成树中*/PToEdgeNode newEdge = (PToEdgeNode)malloc(sizeof(ENode));newEdge->Start = parent[minV];newEdge->End = minV;newEdge->Weight = dis[minV];InsertEdgeInLGraph(MST, newEdge);Vcount++;totalWeight += dis[minV];dis[minV] = FALSE;    //防止重复加入/*更新dis和path数组*/for(int i = 1; i <= MGraph->VertexNums; i++) {if(dis[i] != FALSE && MGraph->Weight[minV][i] < INFINITY) {   //如果i是之前找到的最小顶点的邻接点并且没有收录if(dis[i] > MGraph->Weight[minV][i]) {  //如果收录最小的节点minV后使得节点i到最小生成树MST的距离变小dis[i] = MGraph->Weight[minV][i];   parent[i] = minV;}}}free(newEdge);}free(MST);if(Vcount < MGraph->VertexNums) return NONE;return totalWeight;
}
int main() {int vertexNums, edgeNums;scanf("%d %d", &vertexNums, &edgeNums);MatrixGraph MGraph = BuildMGraph(vertexNums, edgeNums);printf("%d", Prim(MGraph));free(MGraph);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/101558.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android创建签名文件,并获取签名文件MD5,SHA1,SHA256值

一、创建Android签名文件 使用Android Studio开发工具&#xff0c;可视化窗口进行创建 第一步&#xff1a;点击AndroidStudio导航栏上的 Build→Generate Signed Bundle / APK 第二步&#xff1a;选择APK选项 第三步&#xff1a;创建签名文件 第四步&#xff1a;输入创建签名的…

VMware和ubuntu配置Hadoop环境

本博客主要是为了学校课程”大数据与云计算“需要安装Hadoop而写&#xff0c;希望这篇博客对各位阅读这篇博客的人有所帮助。废话不多说&#xff0c;下面直接开始配置教程。 一、获取VMware安装包 VMware获取方法有很多种&#xff0c;这里我准备了官网获取和从我准备的资料中获…

【计算机网络篇】TCP协议

✅作者简介&#xff1a;大家好&#xff0c;我是小杨 &#x1f4c3;个人主页&#xff1a;「小杨」的csdn博客 &#x1f433;希望大家多多支持&#x1f970;一起进步呀&#xff01; TCP协议 1&#xff0c;TCP 简介 TCP&#xff08;Transmission Control Protocol&#xff09;是…

探究HTTP API接口测试:工具、方法与自动化

本文将深入探讨HTTP API接口测试的重要性&#xff0c;并介绍了相关工具、方法以及自动化测试的实施&#xff0c;同时比较了HTTP和API接口测试的区别。从不同角度解析这一关键测试领域&#xff0c;帮助读者更好地理解和应用于实际项目中。 在如今数字化的世界中&#xff0c;软件…

【学会动态规划】摆动序列(27)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

STM32--MPU6050与I2C外设

文章目录 前言MPU6050参数电路MPU6050框图 IIC外设框图 IIC的基本结构软件IIC实现MPU6050硬件IIC实现MPU6050 前言 在51单片机专栏中&#xff0c;用过I2C通信来进行实现AT24C02的数据存储&#xff1b; 里面介绍的是利用程序的编程来实现I2C的时序&#xff0c;进而实现AT24C02与…

k8s-dashboard使用指导手册

一、访问 dashboard http://172.66.209.101:32001 二、选择 Namespace 如下图&#xff1a; 1 在①搜索框中输入 spms 2 在②选择 spms-cloud 三、查找 pod 1 打开 pod 列表 2 打开过滤窗口 3 搜索 pod 在打开的搜索框中输入 pod的关键字&#xff0c;支持模糊搜索 如搜索…

Linux系统USB摄像头测试程序(三)_视频预览

这是在linux上usb摄像头视频预览程序&#xff0c;此程序用到了ffmpeg、sdl2、gtk3组件&#xff0c;程序编译之前应先安装他们。 #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <zconf.h> …

代码随想录算法训练营第四十二天 | 01背包问题,01背包问题(滚动数组),416. 分割等和子集

代码随想录算法训练营第四十二天 01背包问题01 背包二维dp数组01背包 01背包问题(滚动数组)416. 分割等和子集 01背包问题 视频讲解 以下是几种背包&#xff0c;如下&#xff1a; 至于背包九讲其其他背包&#xff0c;面试几乎不会问&#xff0c;都是竞赛级别的了&#xff0c;…

HBuilderX学习--运行第一个项目

HBuilderX&#xff0c;简称HX&#xff0c;是轻如编辑器、强如IDE的合体版本&#xff0c;它及轻巧、极速&#xff0c;强大的语法提示&#xff0c;提供比其他工具更优秀的vue支持大幅提升vue开发效率于一身(具体可看官方详细解释)… 一&#xff0c;HBuilderX下载安装 官网地址 …

mybatis动态SQL的运用

一、mybatis动态SQL update 表名 set name?,age? where id? 如果我们的前台没有传参&#xff0c;比如没有传入我们的name值&#xff0c;name就会把字段值改为null&#xff0c;这就违背了我们编码的初衷。 许多人会使用类似于where 1 1 来作为前缀&#xff0c;在代码中会用i…

企业数字化转型大数据湖一体化平台项目建设方案PPT

导读&#xff1a;原文《企业数字化转型大数据湖一体化平台项目建设方案PPT》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 喜欢文章&#xff0c;您可以点赞评论转发…

Django视图-HttpRequest请求对象和HttpResponse响应对象

文章目录 HttpRequestHttpResponse实践request对象的属性和方法响应 def index(request): 这个request其实就是内部已经封装好的Http请求HttpRequest&#xff0c;它是一个请求对象Django中的视图主要用来接受Web请求&#xff0c;并做出响应。 视图的本质就是一个Python中的函数…

手机debug模式无法连接AndroidStudio,或者Android项目运行失败

在开发中&#xff0c;经常会遇到手机开发模式无法连接AndroidStudio;或者连接后运行失败的问题&#xff0c;请关闭以下设置。

Android学习之路(9) Intent

Intent 是一个消息传递对象&#xff0c;您可以用来从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信&#xff0c;但其基本用例主要包括以下三个&#xff1a; 启动 Activity Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity()&…

SpringMVC拦截器学习笔记

SpringMVC拦截器 拦截器知识 拦截器(Interceptor)用于对URL请求进行前置/后置过滤 Interceptor与Filter用途相似但实现方式不同 Interceptor底层就是基于Spring AOP面向切面编程实现 拦截器开发流程 Maven添加依赖包servlet-api <dependency><groupId>javax.se…

基于Spring Boot的游泳馆管理系统的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的游泳馆管理系统的设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java spring…

阿里云容器镜像服务ACR(Alibaba Cloud Container Registry)推送镜像全过程及总结

前提&#xff1a;安装配置好docker&#xff0c;可参考我这篇 基于CentOS7安装配置docker与docker-compose。 一、设置访问凭证 1.1 容器镜像服务ACR 登录进入阿里云首页&#xff0c;点击 产品-容器-容器镜像服务ACR 点击管理控制台 1.2 进入控制台-点击实例列表 个人容器…

xcode14.3更新一系列问题

1. Missing file libarclite_iphoneos.a (Xcode 14.3) 解决方法 Xcode升级到14.3后编译失败&#xff0c;完整错误日志&#xff1a; File not found: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphoneo…

简单介绍 CPU 的工作原理

内部架构 CPU 的根本任务就是执行指令&#xff0c;对计算机来说最终都是一串由 0 和 1 组成的序列。CPU 从逻辑上可以划分成 3 个模块&#xff0c;分别是控制单元、运算单元和存储单元 。其内部架构如下&#xff1a; 【1】控制单元 控制单元是整个CPU的指挥控制中心&#xff…