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

这篇具有很好参考价值的文章主要介绍了浙大数据结构第八周之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作为弧尾的出边表。

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

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

 从图中可以看出,顶点表的各个结点由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的性质,不过就当巩固所学吧 文章来源地址https://www.toymoban.com/news/detail-667419.html

代码实现:

#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两个节点之间有边,就初始化为权值,否则就初始化为INFINITY
        parent[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;
}

到了这里,关于浙大数据结构第八周之08-图7 公路村村通的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 浙大数据结构第三周之03-树2 List Leaves

    Given a tree, you are supposed to list all the leaves in the order of top down, and left to right. Input Specification: Each input file contains one test case. For each case, the first line gives a positive integer N (≤10) which is the total number of nodes in the tree -- and hence the nodes are numbered from 0 to N−1. Then N lines follow, each corr

    2024年02月16日
    浏览(30)
  • 浙大数据结构第一周01-复杂度3 二分查找

    本题要求实现二分查找算法。 函数接口定义: 其中 List 结构定义如下: L 是用户传入的一个线性表,其中 ElementType 元素可以通过、==、进行比较,并且题目保证传入的数据是递增有序的。函数 BinarySearch 要查找 X 在 Data 中的位置,即数组下标(注意:元素从下标1开始存储)

    2024年02月12日
    浏览(31)
  • 浙大数据结构第二周02-线性结构2 一元多项式的乘法与加法运算

    设计函数分别求两个一元多项式的乘积与和。 输入格式: 输入分2行,每行分别先给出多项式非零项的个数,再以指数递降方式输入一个多项式非零项系数和指数(绝对值均为不超过1000的整数)。数字间以空格分隔。 输出格式: 输出分2行,分别以指数递降方式输出乘积多项式

    2024年02月13日
    浏览(36)
  • 浙大数据结构之04-树4 是否同一棵二叉搜索树

    给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。于是对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜

    2024年02月16日
    浏览(27)
  • 浙大数据结构第四周之04-树6 Complete Binary Search Tree

    A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties: The left subtree of a node contains only nodes with keys less than the node\\\'s key. The right subtree of a node contains only nodes with keys greater than or equal to the node\\\'s key. Both the left and right subtrees must also be binary search trees. A Comple

    2024年02月16日
    浏览(37)
  • (浙大陈越版)数据结构 第三章 树(上) 3.3 二叉树的遍历

    目录 3.3.1 遍历(先中后) 二叉树的遍历 先序遍历: 中序遍历 后序遍历 tips: 3.3.2 中序非递归遍历 非递归算法实现的基本思路:使用堆栈 中序遍历的非递归算法具体实现方法为: 3.3.3 层序遍历 难点 解决方法: 队列实现 思路 有如下二叉树作为例子: 遍历过程:(出队即

    2024年02月06日
    浏览(34)
  • (浙大陈越版)数据结构 第三章 树(中) 二叉搜索树和平衡二叉树

    目录 4.1.1 二叉搜索树及查找 什么是二叉搜索树 定义 二叉搜索树特殊函数集: 查找操作:Find 算法思想 代码实现 补:查找最大和最小元素 4.1.2 二叉搜索树的插入 插入操作:Insert 算法思想 代码实现 例题 4.1.3 二叉搜索树的删除 删除操作:delete 算法思想 情况1:删除叶节点

    2024年02月08日
    浏览(38)
  • 考研数据结构:第八章 排序

    2.1.1算法思想 插入排序的思想很简单,就是不断的把一个个带排序的记录,按的大小插入到前面已经排好序的子序列中。直到全部序列插入完成。 比如我们现在要对下面的序列进行排序, 刚开始我们从1号位开始 我们会认为当前处理的这个元素之前都是有序的 现在把

    2024年02月11日
    浏览(26)
  • 数据结构与算法-第八章 排序技术

    排序:给定一组 记录 (数据元素、结点、顶点)的集合{r 1 , r 2 , …, r n },其相应的关键码分别为{k 1 , k 2 , …, k n },将这些记录排列为{r s1 , r s2 , …, r sn }的序列,使得相应的关键码 满足k s1 ≤k s2 ≤…≤k sn (升序(非降序))(或k s1 ≥k s2 ≥…≥k sn )(降序(非升序)

    2024年02月02日
    浏览(34)
  • 软件工程第八周

    需求分析 :测试 功能 是什么 总体设计:测试 模块之间 是否有调用关系 过程性分析:测试一步一步走的对不对 当设计测试用例时,首先要明确测试的目标和范围,然后根据这些目标来决定具体的测试活动和方法。以下是对三个阶段的扩展描述: 需求分析 测试功能是什么:

    2024年02月07日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包