图论 - 最小生成树 - Kurskal算法 - 道路升级

问题描述

Z国有 n 个城市和 m 条双向道路,每条道路连接了两个不同的城市,保证所有城市之间都可以通过这些道路互达。每条道路都有一个载重量限制,这限制了通过这条道路的货车最大的载重量。道路的编号从 1 至 m 。巧合的是,所有道路的载重量限制恰好都与其编号相同。

现在,要挑选出若干条道路,将它们升级成高速公路,并满足如下要求:

  • 所有城市之间都可以通过高速公路互达。
  • 对于任意两个城市 u,v 和足够聪明的货车司机:只经过高速公路从 u 到达 v 能够装载货物的最大重量,与经过任意道路从 u 到达 v 能够装载货物的最大重量相等。(足够聪明的司机只关注载重量,并不在意绕路)

在上面的前提下,要求选出的道路数目尽可能少。

求需要挑选出哪些道路升级成高速公路(如果有多种方案请任意输出一种)。

输入

第一行 2 个用空格隔开的整数 n,m ,分别表示城市数目、道路数目。

第 2 行到第 m+1 行,每行 2 个用空格隔开的整数 u,v 描述一条从 u 到 v 的双向道路,第 i+1 行的道路的编号为 i 。

注意:数据只保证不存在连接的城市相同的道路(自环),并不保证不存在两条完全相同的边(重边)

输出

第一行一个整数 k ,表示升级成高速公路的道路数。

接下来 k 行每行一个整数,从小到大输出所有选出的道路的编号。

输入样例

3 3
1 2
2 3
1 3

输出样例

2
2
3

数据范围

对于 20% 的数据,保证 n≤5,m≤10。

对于 60% 的数据,保证 n≤1,000,m≤5,000。

对于 100% 的数据,保证 n≤200,000,m≤400,000。

时间限制:10 sec

空间限制:256 MB

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

思路:

此题为最小生成树算法的推广习题,其实是求解最大生成树。

问题抽象:将所有城市看作结点,道路看作边,载重量看作边的权值,则所有城市和道路构成一张连通带权图。根据题目要求,我们可知,题目要从所有给出的边中选择出一些边,这些边要满足以下要求: ① 这些边使所有结点构成一连通图。② 该连通图中任意两个结点间存在一条通路,且该通路上边的权值和是所有原图中这两个点间所有可能通路中路径权值最大的。③ 满足前两条前提下所选择的边尽可能少。

该问题其实是求解原图的一棵最大生成树。类似最小生成树,可用Kurskal算法求解。

C++代码:

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

const int MAX_ROAD = 400001;
int nl[MAX_ROAD], nr[MAX_ROAD];    // nl[i], nr[i]分别记录第i条道路的两个端点 

/* 并查集 */
class DisjointSet
{
    private:
        unsigned int size;   // 并查集中所有端点数,集合中元素分别为 1 ~ n。
        int * a;    // 用于保存并查集,a[i] = j (j > 0) 表示元素i所在集合的父结点是元素j;若a[i] = -k,表示元素i所在的集合其为根,其集合大小为k。
    public:
        DisjointSet() { size = 0; a = NULL; }
        DisjointSet(unsigned int _size)
        {
            size = _size;
            a = new int[size+1];
            memset(a, -1, (size+1)*sizeof(int));    // 初始化各元素各成一个集合,且集合大小均为1。
        }
        ~DisjointSet() { delete [] a; }
        int find(int x)    // 寻找编号为x的元素所在的集合的根,若返回-1表示没找到。
        {
            if ( x <= 0 || x > size )    // 若x超出合法范围
                return -1;
            if ( a[x] < 0 )    // x即为根
                return x;
            a[x] = find(a[x]); // 路径压缩
            return a[x];
        }
        bool unionset(int x, int y)    // 将编号为x和y的元素所在的集合合并,成功则返回true,否则返回false。
        {
            int rx = find(x);
            int ry = find(y);
            if ( rx < 0 || ry < 0 || rx == ry )    // 有任意元素查找失败,或者【两个元素已在同一集合中】则什么也不做
                return false;
            if ( a[rx] < a[ry]  )    // x所在集合规模大
            {
                a[rx] += a[ry];
                a[ry] = rx;
            }
            else
            {
                a[ry] += a[rx];
                a[rx] = ry;
            }
            return true;
        }
}; 

int main()
{
    int n_city = 0, n_road = 0;    // 城市数,道路数
    scanf("%d %d", &n_city, &n_road);    // 读入城市数,道路数 

    for ( int i = 1; i <= n_road; ++i )  // 读入道路i连接的城市
        scanf("%d %d", nl+i, nr+i); 

    DisjointSet c(n_city);
    int n_choice = 0;   // 选择的道路数
    vector<int> ans;    // 存储选择的道路编号,编号从大到小。 

    /* Kruskal算法求最大生成树,由于此题道路权重即编号,故自然有序,不需要用堆存储道路 */
    for ( int i = n_road; n_choice < n_city && i > 0; --i )    // 若n_choice = n_city - 1,则已得到最大生成树,不必继续做下去。
    {
        int sl = c.find(nl[i]), sr = c.find(nr[i]);    // 找到道路两个端点的根
        if ( sl != sr )    // 若道路左右两个城市不在一个集合,则它们选择该边,并将两个集合合并
        {
            ++n_choice;
            ans.push_back(i);
            c.unionset(sl, sr);
        }
    } 

    printf("%d\n", n_choice);
    for ( int i = n_choice - 1; i >=0; --i )
        printf("%d\n", ans[i]);
    return 0;
} 

原文地址:https://www.cnblogs.com/fyqq0403/p/10505920.html

时间: 2024-10-08 16:04:31

图论 - 最小生成树 - Kurskal算法 - 道路升级的相关文章

图论-最小生成树-Kruskal算法

有关概念: 最小生成树:在连通图G中,连接图G所有顶点且总权最小的边构成的树 思路: 首先对边按权从小到大排序,紧接着枚举每一条边,如果两个结点的祖先结点不同(并查集),则连上此边,直到边数等于结点数-1即可 邻接矩阵输入,用类邻接表存储方式存边 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 #define MAXN 5 #define MAXM 6 int father[MAXN],n,m,cn

算法训练营:道路升级

问题描述 Z国有 n 个城市和 m 条双向道路,每条道路连接了两个不同的城市,保证所有城市之间都可以通过这些道路互达.每条道路都有一个载重量限制,这限制了通过这条道路的货车最大的载重量.道路的编号从 1 至 m .巧合的是,所有道路的载重量限制恰好都与其编号相同. 现在,要挑选出若干条道路,将它们升级成高速公路,并满足如下要求: 所有城市之间都可以通过高速公路互达. 对于任意两个城市 u,v 和足够聪明的货车司机:只经过高速公路从 u 到达 v 能够装载货物的最大重量,与经过任意道路从 u 到达

hpuoj 问题 C: 善良的国王【最小生成树kurskal】

问题 C: 善良的国王 时间限制: 1 Sec  内存限制: 128 MB提交: 112  解决: 48[提交][状态][讨论版] 题目描述 很久很久以前,有一个贫困的国家,这个国家有一个善良爱民的国王,但是国运维艰,这个国家总是不得不面对这天灾的严峻挑战,又一次连月的大雨,引发了洪水,洪水冲断了道路,水褪去后也有很多村庄成为了孤岛,善良的国王爱民如子,于是他想从本不富足的税收中拿出一部分钱,来给这些村庄修道路,但是国力有限,不能修复所有的道路,于是国王决定,保证村庄两两之间可以到达就好.现在国

HDU 1162 Eddy&#39;s picture(图论-最小生成树)

题目如下: Eddy's picture Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7267    Accepted Submission(s): 3676 Problem Description Eddy begins to like painting pictures recently ,he is sure of himse

最小生成树のprim算法

Problem A Time Limit : 1000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 31   Accepted Submission(s) : 10 Problem Description 省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可).经过调查评估,得到的统计表中列出了有可能建设公

Kurskal算法(克鲁斯卡尔算法)

特点:适用于稀疏图,边比较少的图.如果顶点较少,且为稠密图,则用Prim算法.跟Prim算法的用途相同.时间复杂度为O(e*loge),其中e为边数. 代码: #include <stdio.h> #include <stdlib.h> #define MAXEDGE 20 //设定边的最大值 #define INF 65535 //用来设定边的最大值 typedef struct Edge { int begin; int end; int weight; }Edge; //构建

hihocoer - 1097(最小生成树&#183;Prim算法)

题目链接: http://hihocoder.com/problemset/problem/1097 题目: #1097 : 最小生成树一·Prim算法 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 最近,小Hi很喜欢玩的一款游戏模拟城市开放出了新Mod,在这个Mod中,玩家可以拥有不止一个城市了! 但是,问题也接踵而来——小Hi现在手上拥有N座城市,且已知这N座城市中任意两座城市之间建造道路所需要的费用,小Hi希望知道,最少花费多少就可以使得任意两座城市都可以通

图论-最小生成树

今天听了CLRS的第二作者讲的课程,关于最小生成树的算法. 其实就是先模拟一下小样例(不是单纯模拟,而是发现其中的规律,要思考) 然后发现最优子结构-如果(u,v)是一条唯一连接两点的边,那么将原图拆分为两块(一块包含u,一块包含v),两图分别最优解+dist[u,v]就是原图的最优解了. 然后发现这样做会有很多很多的重叠子问题:把每次的(u,v)换一换顺序就一堆重叠子问题.于是就有动态规划的思路了-- 别急! 这里还有一个性质:如果(u,v)在当前图中边权最小,则(u,v)必在此图的最小生成树

SOJ4339 Driving Range 最小生成树 kruskal算法

典型的最小生成树 然后求最大的一条边 附上链接 http://cstest.scu.edu.cn/soj/problem.action?id=4339 需要注意的是有可能有 "IMPOSSIBLE" 的情况 这里用一个flag标记 记录所并的节点 只有flag = n时才能成功 负责就 "IMPOSSIBLE" 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring&g