最小生成树的求法无非就两种方法:一种是prim算法,一种是kruscal算法
prim算法类似于迪杰斯特拉算法,时间复杂度为O(n^2)
kruscal算法是每次枚举最小边,并查集检验是否成环即可,时间复杂度为O(elog(e))
两种方法都非常简单。。。。
现在扩展一种知识点,叫做次小生成树:
定义:给定一个带权图,把图中所有生成树按权值从小到大排序,第二小的成为次小生成树
计算次小生成树的两种方法:
方法1:先求最小生成树,再枚举删去最小生成树中的边求解,时间复杂度O(mlogm+n*m)
方法2:先求最小生成树,然后依次枚举非树边,然后将该边加入树中,同时从树中去掉一条边, 使得到的图仍是一棵树,则一定可以求出次小生成树
设T为图G的一颗生成树,对于非树边a和树边b,插入边a,并删除边b的操作即为(+a,-b)。
如果T+a-b之后,仍然是一颗生成树,称(+a,-b)是T的一个可行交换。
称由T进行一次可行变换所得到的新的生成树集合成为T的邻集。
定理:次小生成树一定在最小生成树的邻集中。
操作步骤:
1.求最小生成树,统计标记每条边是树边,还是非树边;同时把最小生成树建立出来
2.预处理任意两点间的边权最大值dis1[a][b]和次大边权dis2[a][b];(dfs遍历两点之间所有边权)
3.依次枚举所有非树边,看是否大于非树边所对应的起点a和终点b的边权最大值dis1[a][b],如果大于,那么就替换,否则再检查是否大于次大边权值dis2[a][b],操作与上亦同,由于非树边的权值可能与最大值相等,所以,要再考虑次大值。
例题:
acwing–1148. 秘密的牛奶运输
农夫约翰要把他的牛奶运输到各个销售点。
运输过程中,可以先把牛奶运输到一些销售点,再由这些销售点分别运输到其他销售点。
运输的总距离越小,运输的成本也就越低。
低成本的运输是农夫约翰所希望的。
不过,他并不想让他的竞争对手知道他具体的运输方案,所以他希望采用费用第二小的运输方案而不是最小的。
现在请你帮忙找到该运输方案。
注意:
如果两个方案至少有一条边不同,则我们认为是不同方案;
费用第二小的方案在数值上一定要严格小于费用最小的方案;
答案保证一定有解;
输入格式
第一行是两个整数 N,M,表示销售点数和交通线路数;
接下来 M 行每行 3 个整数x,y,z,表示销售点 x 和销售点 y 之间存在线路,长度为 z。输出格式
输出费用第二小的运输方案的运输总距离。数据范围
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OhCJsz5R-1628304561944)(/img/bVcSzEF)]
数据中可能包含重边。输入样例:
4 4
1 2 100
2 4 200
2 3 250
3 4 100
输出样例:
450
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
struct Edge
{
int x,y,w;
bool op;
bool operator<(const Edge &a)const{
return w<a.w;
}
}e[10001];
struct node
{
int to,w;
};
vector<node>v[501];
int parent[501];
int Find(int x)
{
if(x!=parent[x])
{
parent[x]=Find(parent[x]);
}
return parent[x];
}
int dist1[501][501],dist2[501][501];//记录两点之间边权最大值和次大值
void dfs(int u,int fa,int maxd1,int maxd2,int d1[],int d2[])//dfs找寻两节点之间最大边权值和次大边权值
{
d1[u]=maxd1,d2[u]=maxd2;
for(int i=0;i<v[u].size();i++)
{
int j=v[u][i].to;
int w=v[u][i].w;
if(j!=fa)//避免死循环
{
int t1=maxd1,t2=maxd2;
//注意一定不可以相等,因为次小生成树要严格大于最小生成树
if(w>maxd1)
{
t1=w;
t2=maxd1;
}
else if(w<maxd1&&w>maxd2)
{
t2=w;
}
dfs(j,u,t1,t2,d1,d2);
}
}
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
parent[i]=i;
}
for(int i=0;i<m;i++)
{
cin>>e[i].x>>e[i].y>>e[i].w;
}
sort(e,e+m);
long long int sum=0;
for(int i=0;i<m;i++)
{
int x=e[i].x,y=e[i].y;
int tx=Find(x),ty=Find(y);
if(tx!=ty)
{
parent[tx]=ty;
e[i].op=1;
sum+=e[i].w;
v[e[i].x].push_back({e[i].y,e[i].w});
v[e[i].y].push_back({e[i].x,e[i].w});
}
}
for(int i=1;i<=n;i++)
{
dfs(i,-1,-0x3f3f3f3f,-0x3f3f3f3f,dist1[i],dist2[i]);
}
long long int ret=1e18;
for(int i=0;i<m;i++)
{
if(!e[i].op)
{
if(e[i].w>dist1[e[i].x][e[i].y])
{
ret=min(ret,sum-dist1[e[i].x][e[i].y]+e[i].w);
}
else if(e[i].w>dist2[e[i].x][e[i].y])
{
ret=min(ret,sum-dist2[e[i].x][e[i].y]+e[i].w);
}
}
}
cout<<ret<<endl;
return 0;
}