最大伪森林——kruskal算法活用 (HDU - 3367)
最大伪森林——kruskal算法活用 (HDU - 3367)
- kruskal这一用来求生成树的算法,经过修改拓展之后,可以求很多种形式的子图,本题(HDU3367)即为一个应用案例
- 以下是原题内容
Pseudoforest
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 4058 Accepted Submission(s): 1615
Problem Description
In graph theory, a pseudoforest is an undirected graph in which every connected component has at most one cycle. The maximal pseudoforests of G are the pseudoforest subgraphs of G that are not contained within any larger pseudoforest of G. A pesudoforest is larger than another if and only if the total value of the edges is greater than another one’s.
Input
The input consists of multiple test cases. The first line of each test case contains two integers, n(0 < n <= 10000), m(0 <= m <= 100000), which are the number of the vertexes and the number of the edges. The next m lines, each line consists of three integers, u, v, c, which means there is an edge with value c (0 < c <= 10000) between u and v. You can assume that there are no loop and no multiple edges.
The last test case is followed by a line containing two zeros, which means the end of the input.
Output
Output the sum of the value of the edges of the maximum pesudoforest.
Sample Input
3 3
0 1 1
1 2 1
2 0 1
4 5
0 1 1
1 2 1
2 3 1
3 0 1
0 2 2
0 0
Sample Output
3
5
Source
“光庭杯”第五届华中北区程序设计邀请赛 暨 WHU第八届程序设计竞赛
Recommend
lcy
题意
本题介绍了一个“伪森林”的概念:
- 对一个无向图,如果它的一个子图满足每个连通分量内部至多有一个环,那么这一个子图就是该无向图的伪森林。
- 类比于最大生成树,如果在这个无向图中,某个伪森林所具有的边权之和大于等于所有此无向图的伪森林,那么该伪森林就叫做最大伪森林。
给我们一个无向图,要求我们求出它的最大伪森林所具有的边权之和。
思路
先思考如果每个连通分量没有环的情况,此时也就是求最大生成森林,此时由于并查集的重要作用,所以用kruskal算法非常合适
现在这个生成森林可以让每棵树至多存在一个环,显然我们的结果应该比简单的生成森林还要大一些。但是我们这样放弃kruskal算法是可惜的,我们可以对它进行修改来求出这个最大伪森林。
我们可能很容易想到,在kruskal算法算得最大生成森林之后,再把所有的边扫一遍,把上一次没有被选中的边(毫无疑问,这条边一定会构成一个环)再加入森林,同时通过并查集的祖先找到它所在的那棵树,标记这颗树为“有环”,这样之后每次判断没有环才会添加,就不会出现两个环了
- 个人感觉上面我这个自己想出来的方法很具有迷惑性,因为由于边已经排好了序,容易让人认为没有更优情况了。不过仔细推敲就能发现反例,如下图:

它的最大生成树是什么?(也就是说,我们第一步做完之后,得到的结果是什么?毫无疑问是下图:

那之后我们第二步加边之后 他会变成如下的一个伪森林:

这是最大伪森林? 不是,下面这个才是:

这就否定了我们的想法
- 显然我们的设想是片面的,如果进行一次kruskal的最大生成森林,那么我们可以知道,出现的图都是“尽可能连通的”,也就是为了连通性牺牲了总权值。
但是最大伪森林不需要这样的牺牲,它完全可以去掉一条边来生成两棵树,进而选择到边权更大的边,只要每棵树不超过一个环,也就是可以牺牲一次连通性来换取更大的边权。
改进思路
- 注意我们在上面提到过“可以牺牲一次连通性来换取更大的边权。”,其实这个过程我们完全可以在kruskal算法的内部修改实现:
我们考虑把每棵树的有环情况实时记录,使用bk2数组,bk2[i]为true 表示并查集祖先为i的树目前有环。
仍然是边排序之后从头进行扫描,每次先路径压缩找到两个点所在树的祖先,然后通过bk2数组得知这两棵树的有无环情况。如果:
1.都是false无环
那么显然这条边可以被选中加入森林(不会违背任何条件)
要注意:如果这两棵树是同一颗,那么要标记这棵树为有环2.两棵都有环
那么不可以加入这条边,否则将会使生成的一棵树中有两个环3.一棵有环一棵无环
那么根据伪森林的定义,我们可以把这条边选入,但是要注意在之后把
要注意:如果这两棵树是同一棵,那么不可以加入(但事实上不可能出现这种情况,否则标记就是错误的)
- 其实实现上述逻辑之后,我们就已经成功地应用了伪森林的定义,结果即为最大伪森林的边权之和
AC代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int n, m, x, y, z, co = 1;
long long sum = 0;
struct ab //边
{
int u, v, w;
} aa[200005];
bool bk2[10005] = {0}; //某棵树有无环
int f[10005] = {0}; //并查集
bool cmp(ab a, ab b)
{
return a.w > b.w;
}
int fd(int xx) //路径压缩
{
if (f[xx] != xx)
{
f[xx] = fd(f[xx]);
}
return f[xx];
}
int main()
{
while (1)
{
scanf("%d%d", &n, &m);
if (!n && !m)
{
break;
}
memset(bk2, 0, sizeof(bk2));
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &x, &y, &z);
aa[i].u = x;
aa[i].v = y;
aa[i].w = z;
}
for (int i = 0; i <= n; i++)
{
f[i] = i;
}
sort(aa + 1, aa + m + 1, cmp);
sum = 0;
for (int i = 1; i <= m; i++) //生成最大伪森林
{
if (fd(aa[i].u) == fd(aa[i].v))
{
if (bk2[f[aa[i].u]] || bk2[f[aa[i].v]])
{
continue;
}
else
{
bk2[f[aa[i].u]] = bk2[f[aa[i].v]] = true;
sum += aa[i].w;
}
}
else
{
if (bk2[f[aa[i].u]] && bk2[f[aa[i].v]])
{
continue;
}
sum += aa[i].w;
if (bk2[f[aa[i].u]] || bk2[f[aa[i].v]])
{
bk2[f[aa[i].u]] = true;
bk2[f[aa[i].v]] = true;
}
f[f[aa[i].v]] = f[aa[i].u];
}
}
printf("%lld\n", sum);
}
return 0;
}
解决本题的关键是理解好kruskal算法的性质(连通性为先,边权追求其次)以及对不同情况的全面分析
最大伪森林——kruskal算法活用 (HDU - 3367)的更多相关文章
- HDU 3367 (伪森林,克鲁斯卡尔)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=3367 Pseudoforest Time Limit: 10000/5000 MS (Java/Oth ...
- 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...
- 最小生成树之 prim算法和kruskal算法(以 hdu 1863为例)
最小生成树的性质 MST性质:设G = (V,E)是连通带权图,U是V的真子集.如果(u,v)∈E,且u∈U,v∈V-U,且在所有这样的边中, (u,v)的权c[u][v]最小,那么一定存在G的一棵最 ...
- HDU 1233 还是畅通工程(Kruskal算法)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1233 还是畅通工程 Time Limit: 4000/2000 MS (Java/Others) ...
- hdu 1162 Eddy's picture (Kruskal 算法)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1162 Eddy's picture Time Limit: 2000/1000 MS (Java/Ot ...
- hdu 1162 Eddy's picture (Kruskal算法,prim算法,最小生成树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1162 [题目大意] 给你n个点的坐标,让你找到联通n个点的一种方法.保证联通的线路最短,典型的最小生成 ...
- HDU 2682 Tree(Kruskal算法求解MST)
题目: There are N (2<=N<=600) cities,each has a value of happiness,we consider two cities A and ...
- HDU 5253 连接的管道(Kruskal算法求解MST)
题目: 老 Jack 有一片农田,以往几年都是靠天吃饭的.但是今年老天格外的不开眼,大旱.所以老 Jack 决定用管道将他的所有相邻的农田全部都串联起来,这样他就可以从远处引水过来进行灌溉了.当老 J ...
- hdu 1233 还是畅通工程 最小生成树(prim算法 + kruskal算法)
还是畅通工程 Time Limit: 4000/2 ...
- hdu 1875 畅通project再续(kruskal算法计算最小生成树)
畅通project再续 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
随机推荐
- P8679 [蓝桥杯 2019 省 B] 填空问题 题解
P8679 [蓝桥杯 2019 省 B] 填空问题 题解 题目传送门 欢迎大家指出错误并联系这个蒟蒻 更新日志 2023-05-25 21:02 文章完成 2023-05-27 11:34 文章通过审 ...
- ts 终于搞懂TS中的泛型啦! | typescript 入门指南 04
大家好,我是王天~ 这篇文章是 ts入门指南系列中第四篇,主要讲解ts中的泛型应用,泛型在ts中是比较重要的概念,我花挺长时间才搞明白的,希望能帮助到大家 ~ ** ts 入门指南系列 ** Ts和J ...
- Sentinel源码改造,实现Nacos双向通信!
Sentinel Dashboard(控制台)默认情况下,只能将配置规则保存到内存中,这样就会导致 Sentinel Dashboard 重启后配置规则丢失的情况,因此我们需要将规则保存到某种数据源中 ...
- 给wordpress后台侧栏菜单添加自定义字段的方法
我们在使用wordpress做网站的时候,难免有一些需要在后台设置侧栏菜单下添加自定义字段的情况.下面就简单说说一下,如何在后台设置侧栏菜单下添加自定义字段? 在这里我们主要是使用wordpress的 ...
- 麒麟系统开发笔记(十三):在国产麒麟系统上编译OSG库、搭建基础开发环境和移植测试Demo
前言 在国产麒麟系统上实现C++三维仿真,使用OSG技术,其他基于web的技术也是可以但是交互上鼠标拽托等交互相对差一些,所以这块需要斟酌选择到底是何种技术来取舍. 本篇在厂家指定的麒麟系统上编 ...
- C++基础杂记(2)
将数组传入函数 禁止修改数组的值 函数的地址与函数的指针 函数的指针数组 函数的 static 与 inline 引用左值和引用传参 C++11 的数组 for 循环 64 位 Linux 操作系统中 ...
- CAP 定理的含义(转)
分布式系统(distributed system)正变得越来越重要,大型网站几乎都是分布式的. 分布式系统的最大难点,就是各个节点的状态如何同步.CAP 定理是这方面的基本定理,也是理解分布式系统的起 ...
- [Python急救站课程]日期和时间的输出
日期和时间的输出 from datetime import datetime # 引用datetime 库 now = datetime.now() # 获得当前日期和时间信息 print(now) ...
- linux其他命令(查找,软链接,打包和压缩,软件安装)笔记
1,查找文件 * 是通配符,代表任意字符,0到多个. find 路径 -name "*.txt" : 查找在路径下所有以 .txt 结尾的文件. 2,软链接 (1)将桌面目 ...
- 03Java学习_注释和代码规范
注释和代码规范 目录 注释和代码规范 注释 注释介绍 单行注释 多行注释 文档注释 代码规范 注释 注释介绍 用于注解说明解释程序的文字就是注释,注释提高了代码的阅读性(可读性):注释 是一个程序员必 ...