生成树
【最小生成树】:
简简单单得这两种算法也就不再赘述了,可以回顾博客最小生成树解析 , 这篇博文讲的就很详细,这里也就只附代码:
\(code : \ \ kruscal\):
\(code : \ \ prim\)
【次小生成树】:
主要的是这一个算法;
前置知识 : 基础图论和最小生成树,见上文,基础图论无;
【求解】:
1.进行\(DFS\)求出所有的生成树,然后进行比较 , 求解第二大的能够达到所有点的值,(没写过)
2.考虑最小生成树 的算法扩展 ;
考虑\(kruscal\)的思路,排序选出\(n-1\)递增的边(从最小的开始),然后重构树形成最小生成树。
同时考虑次小生成树的求解,枚举最小生成树的每一条边,然后砍去最小生成树的一条边,让它走其他的边,那么这时候也就形成了次小生成树,同时,很明显,从最小生成树上只删去一条边是要比删去两条边更优的,所以,它的求解算法就是 , 先求出最小生成树,然后枚举删去生成树上的边,然后再跑一个\(Kruscal\)求解现在的最小生成树,然后进行比较,得出第二小的最小生成树边权和。
然后你就得到了这么的一个代码:
然后这只能够得到50分的高分
/*
by : Zmonarch
知识点 : 最小生成树
思路 : 很显然,是一个次小生成树
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <set>
#define int long long
using namespace std ;
const int kmaxn = 1e6 ;
inline int read()
{
int x = 0 , f = 1 ; char ch = getchar() ;
while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
return x * f ;
}
int n , m , ans1 = 0 , ans2 = 1e18 , pos;
struct Node
{
int nxt , to , weath ;
}edge[kmaxn] ;
int point_to[kmaxn] ;
bool vis[kmaxn] ;
bool cmp(Node x , Node y)
{
return x.weath < y.weath ;
}
int findx(int x)
{
if(x == point_to[x]) return x ;
else return point_to[x] = findx(point_to[x]) ;
}
int number ;
void kruscal1()
{
sort(edge + 1 , edge + m + 1 , cmp) ;
for(int i = 1 ; i <= n ; i++) point_to[i] = i ;
for(int i = 1 ; i <= m ; i++)
{
int fx = findx(edge[i].nxt ) ;
int fv = findx(edge[i].to) ;
if(fx != fv)
{
vis[i] = true ; //表示这一条边是最小生成树上的边
point_to[fx] = fv ;
number ++ ;
ans1 += edge[i].weath;//正常跑出最小生成树即可
}
if(number == n - 1 ) break ;
}
}
int kruscal2()
{
number = 0 ;
int ret = 0 , flag = 0 ;
sort(edge + 1 , edge + m + 1 , cmp) ;
for(int i = 1 ; i <= n ; i++) point_to[i] = i ;
for(int i = 1 ; i <= m ; i++)
{
if(pos == i) continue ; //枚举的那一条边不要了
int fx = findx(edge[i].nxt) ; //正常的最小生成树
int fv = findx(edge[i].to) ;
if(fx != fv)
{
point_to[fx] = fv ;
number ++ ;
ret += edge[i].weath ;
}
if(number == n - 1 )
{
flag = 1 ; //首先
break ;
}
}
if(flag) return ret ;
else return (int)21020100000110 ; //返回一个最大值
}
signed main()
{
n = read() , m =read() ;
for(int i = 1 ; i <= m ; i++)
{
edge[i].nxt = read() , edge[i].to = read() , edge[i].weath = read();
}
kruscal1() ;//先让他去跑一个最小生成树
//printf("%d\n" , ans1) ;
for(int i = 1 ; i <= m ; i++)
{
if(vis[i])
{
pos = i ;//枚举删去的边
int opt = kruscal2() ;
// printf("%lld\n" , opt) ;
if(opt == ans1) continue ;//求解的不是最小生成树
ans2 = min(ans2 , opt) ;//求解第二小
}
}
printf("%lld" , ans2) ;
return 0 ;
}
复杂度更加优秀的:倍增加LCA。
有关其复杂度足够优秀的,笔者还在学习,