题目描述
农夫约翰要把他的牛奶运输到各个销售点。
运输过程中,可以先把牛奶运输到一些销售点,再由这些销售点分别运输到其他销售点。
运输的总距离越小,运输的成本也就越低。
低成本的运输是农夫约翰所希望的。
不过,他并不想让他的竞争对手知道他具体的运输方案,所以他希望采用费用第二小的运输方案而不是最小的。
现在请你帮忙找到该运输方案。
注意:
如果两个方案至少有一条边不同,则我们认为是不同方案;
费用第二小的方案在数值上一定要严格小于费用最小的方案;
答案保证一定有解;
输入格式
第一行是两个整数 N,M,表示销售点数和交通线路数;
接下来 M行每行 3 个整数 x,y,z,表示销售点 x 和销售点 y 之间存在线路,长度为 z。
输出格式
输出费用第二小的运输方案的运输总距离。
数据范围
1≤N≤500,
1≤M≤104,
1≤z≤109,
数据中可能包含重边。
样例
输入样例:
4 4
1 2 100
2 4 200
2 3 250
3 4 100
输出样例:
450
严格次小生成树
求次小生成树,两种方法:
1. 枚举树边,去除,再做Kruskal
2. 枚举非树边,加入树,成环,那么去除环上权值最大的边
但是第一种对于求严格次小生成树不方便
使用第二种,我们可以先初始化最小生成树上任意两点间的路径上权值最大的边是多少,利用DFS即可,并在建立树的时候对是否是最小生成树的树边进行标记
初始化最大的权值是不够的,因为可能会有枚举到的边与之相等的情况,那么就无法更新,所以还要记录一个严格次小值
C++ 代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510,M=10010;
typedef long long LL;
int n,m;
LL sum;
int p[N];
int dist1[N][N],dist2[N][N];
int e[2*N],ne[2*N],w[2*N],h[N],idx;//记录最小生成树,方便进行DFS
struct Node{
int a,b,we;
bool f;
bool operator<(const Node &t)const{
return we<t.we;
}
}ed[M];
void add(int a,int b,int we){
e[idx]=b,ne[idx]=h[a],w[idx]=we,h[a]=idx++;
}
int find(int x){
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void dfs(int u,int fa,int maxd1,int maxd2,int d1[],int d2[]){
//u是当前节点,fa是u的父亲,maxn当前走过的路径上的最大权值,d[]
d1[u]=maxd1,d2[u]=maxd2;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa) continue;//不可以走回头路
int md1=maxd1,md2=maxd2;
if(w[i]>md1) md2=md1,md1=w[i];
//这里一定要把两个值新开变量存储起来
//因为从u后继的每一条边都要用这两个值,要保证在后继更新的时候是同一起点
else if(w[i]<md1&&w[i]>md2) md2=w[i];
//一定要加上w[i]<md1,否则可能d1和d2会相等,那就不是严格小了
dfs(j,u,md1,md2,d1,d2);
}
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++) p[i]=i;
for(int i=0;i<m;i++){
int a,b,we;
cin>>a>>b>>we;
ed[i]={a,b,we};
}
sort(ed,ed+m);
for(int i=0;i<m;i++){
int a=find(ed[i].a),b=find(ed[i].b);
if(a!=b){
sum+=ed[i].we;
p[a]=b;
add(ed[i].a,ed[i].b,ed[i].we);
add(ed[i].b,ed[i].a,ed[i].we);
ed[i].f=true;//记录是最小生成树中的边
}
}
LL ans=1e18;
for(int i=1;i<=n;i++) dfs(i,-1,0,-1e9,dist1[i],dist2[i]);//对于每一个节点到其他可到达节点的距离
for(int i=0;i<m;i++){
if(ed[i].f) continue;
int a=ed[i].a,b=ed[i].b;
LL t;
if(ed[i].we>dist1[a][b])
t=sum+ed[i].we-dist1[a][b];
else if(ed[i].we>dist2[a][b])
t=sum+ed[i].we-dist2[a][b];
ans=min(ans,t);
}
cout<<ans;
return 0;
}