生成树

【最小生成树】:

简简单单得这两种算法也就不再赘述了,可以回顾博客最小生成树解析 , 这篇博文讲的就很详细,这里也就只附代码:

\(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。

有关其复杂度足够优秀的,笔者还在学习,

posted @ 2021-01-10 16:05  SkyFairy  阅读(91)  评论(0编辑  收藏  举报