[BNDSOJ-397]唯一的最小生成树 题解

研究了半天,最后还是暴搜解决的问题——题记

原题

给出一个无向连通图,问最小生成树是不是唯一的.
输入:第一行一个整数t(1 <= t <= 5), 表示测试数据的组数. 每组数据描述一个无向连通图,第一行是两个整数 n 和 m (1 <= n <= 100), 图中的点的个数和边数. 下面m行给出每条边(xi, yi, wi), 表示点xi和yi间有一条长为wi的边。
输出:对于每组输入,如果最小生成树是唯一的输出最小花费,否则输出’Not Unique!’.

思路

思考过程

对于这种的题,kruskal算法肯定是跑不了了······但是怎么去理解这个东西的数学本质就是一个有一定难度的问题了。就算是理解了,你还是很难避开这道题的一大天坑。
先说说我一开始的思路——以我个人的想法,如果我们在构树的时候能够随时考虑一下相等长度的边的话,即遇到某一长度的边,就试着将所有与其长度相等的边加入,看看是否有两条边能够连接同样的两个并查集就可以了。这样的思路实现起来其实并不难,因为并查集本身就是做这种事情的。
但是,这样真的能够解决所有的情况吗?
事实证明,这不靠谱
事实证明,不太靠谱······
一时间,我陷入了困境。所以到底是什么情况没有考虑到呢?
此时secretpal一句话道破了真相——原来边的输入顺序是不定的,所以在kruskal算法添加边的时候是存在一种在后期会被接入并查集的节点,而这种节点在前期可能被一条算法中没有用到的边连接到。为了方便大家理解,我可以试着用图片表示一下
在这里插入图片描述

如图所示,kruskal面对这种情况时就很尴尬了,事实上图4的情况中如果把先接上的那条边替换为被kurskal算法忽略(画叉)的那条边也是可以的,但是kruskal就是想不到这种情况。这样的情况也是多解的,但是我第一次的思路并没有考虑到这种情况。因此导致了大片WA。

最终思路

之后,在secretpal的提示下,我先构建完了一个可行的最小生成树,然后在我自行的思考下发现了一个规律——既然是树,那么就意味着我们可以通过唯一的一条路径连接两个节点,结合本题的数据,也证明了本题后期可以采用DFS这样简单粗暴的方法来寻找这条路径。如果我们加了任意一条边,就会形成环。在环中如果有一对等长的边,就说明这个图中的两条边可以互相替换。只要我们将没有用到的边的两端作为DFS的起点的终点,在搜索的时候检查路径上的边有无代价和没有被用到的边相等的边,就可以了。

大体思路下的一些代码小细节

数据

由于这道题中的数据肯定不止一组,为了节省一些空间,大家难免会将不少数组、数据变量等进行重复使用,这个时候每次循环间数据的重置就是我们要格外注意的。根据循环嵌套关系的不同,每种变量或数组都有它要更新的频率。这一点我们一定不能大意。评测机上开的新数组内数据是不固定的,这就要求我们在运算开始前将数组用memset或者手写for来清理好。像并查集表示、最小代价这样的数组每进行完一轮数据就要清空一次。存储边的结构体数组可以不清空,但是循环取用数据的时候一定不能多取,不然就会产生一些无中生有的边,一定概率会导致WA。像检查最小生成树是否构成连通图的CNT数组,一定要每增加一条边清空一次,不然100%的测试点都会WA。

输出提速

最后输出的时候,总有那么一些没用上的边是我们一眼就能看出来结论的。比如数据中出现的重边,就意味着我们可以直接下结论说最小生成树不唯一,如果存在起点终点都在同一个节点的边,那么这条边是可以不用考虑代替关系问题的。因为这样的边没有任何替代价值,并且这条边一旦加入,这个图就不是树了,而是环!多写几个if对于处理那些特别奇怪的数据提速有奇效,还减少了DFS的次数。

AC代码

/*由于博主日常生活学习时间紧迫,今天的代码注释实在没时间写了······
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
struct edge{
   
	int sa;
	int sb;
	int dis;
};
edge ed[5000];
bool used[5000];
int up[101];
int cnt[101];
int smalltree[101][101];
int maxcnt;
int spd;
bool uproute[101];
int t,n,m;//point and edge
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,CSP-J2021复赛有两道题目,分别是分糖果和小熊的果篮。 对于第一题分糖果,题目来源是CCF,难度为入门。根据给出的代码,这是一个基于循环的算法,通过遍历[l,r]区间内的数,计算数对n取模后的最大值。具体的实现细节可以参考引用中的代码。这道题目属于入门级别,比较简单。 第二题是关于小熊的果篮。给定一个长度为n的数组a,其中连续的相同元素被视为一个块,要求按照块的顺序输出每个块的头元素,并删除已输出的元素。具体的实现细节可以参考引用中的代码。这道题目需要使用双链表来处理,时间复杂度为O(n)。 综上所述,CSP-J2021复赛的题目包括分糖果和小熊的果篮,具体的解题思路和代码实现可以参考上述引用内容。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [[CSP-J 2021]比赛题解](https://blog.csdn.net/weixin_56550385/article/details/126811201)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [新鲜出炉的 CSP-J 2021 复赛题目 题解](https://blog.csdn.net/qq_23109971/article/details/121024436)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值