AcWing 1148. 秘密的牛奶运输 (严格次小生成树)

 

整理的算法模板:ACM算法模板总结(分类详细版)

 

农夫约翰要把他的牛奶运输到各个销售点。

运输过程中,可以先把牛奶运输到一些销售点,再由这些销售点分别运输到其他销售点。

运输的总距离越小,运输的成本也就越低。

低成本的运输是农夫约翰所希望的。

不过,他并不想让他的竞争对手知道他具体的运输方案,所以他希望采用费用第二小的运输方案而不是最小的。

现在请你帮忙找到该运输方案。

注意:

  • 如果两个方案至少有一条边不同,则我们认为是不同方案;
  • 费用第二小的方案在数值上一定要严格小于费用最小的方案;
  • 答案保证一定有解;

输入格式

第一行是两个整数 N,MN,M,表示销售点数和交通线路数;

接下来 MM 行每行 33 个整数 x,y,zx,y,z,表示销售点 xx 和销售点 yy 之间存在线路,长度为 zz。

输出格式

输出费用第二小的运输方案的运输总距离。

数据范围

1≤N≤5001≤N≤500,
1≤M≤1041≤M≤104,
1≤z≤1091≤z≤109,
数据中可能包含重边。

输入样例:

4 4
1 2 100
2 4 200
2 3 250
3 4 100

输出样例:  

450

定义:   给一个带权的图,把图的所有生成树按权值从小到大排序,第二小的称为次小生成树。

  • 方法1: 先求最小生成树,再枚举删去最小生成树中的边求解。时间复杂度。O(mlogm + nm)

 

  • 方法2: 先求最小生成树,然后依次枚举非树边,然后将该边加入树中,同时从树中去掉一条边, 使得最终的图仍是一棵树。则一定可以求出次小生成树。
     

设T为图G的一棵生成树,对于非树边a和树边b,插入边a,并删除边b的操作记为(+a, -b)。
如果T+a-b之后,仍然是一棵生成树, 称(+a,-b)是T的一 个可行交换。
称由T进行一次可行变换所得到的新的生成树集合称为T的邻集。
定理:次小生成树一定在最小生成树的邻集中。

具体做法:

(这里需要注意的是,由于树外边有可能等于两个点之边的最大值,这个时候不但要求出来两点之间最大权边,还要求出次大权边)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=510,M=(1e4+7)*2;
int dis1[N][N],dis2[N][N],n,m,idx;
int h[M],e[M],ne[M],w[M],p[N];
struct node
{
    int a,b,c;
    bool flag;
}egdes[M/2];
bool cmp(node a,node b)
{
    return a.c<b.c;
}
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],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 max_1d,int max_2d,int d1[],int d2[])
{
    
    d1[u]=max_1d,d2[u]=max_2d;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j==fa) continue;
        int maxt1=max_1d,maxt2=max_2d;
        if(w[i]>maxt1) maxt2=maxt1,maxt1=w[i];
        else if(w[i]<maxt1&&w[i]>maxt2) maxt2=w[i];
        dfs(j,u,maxt1,maxt2,d1,d2);
    }
}
int main()
{
    memset(h,-1,sizeof h);
    cin >>n>>m;
    for(int i=0;i<=n;i++) p[i]=i;
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin >>a>>b>>c;
        egdes[i]={a,b,c};
    }
    sort(egdes,egdes+m,cmp);
    ll sum=0;
    for(int i=0;i<m;i++)
    {
        int a=find(egdes[i].a),b=find(egdes[i].b),c=egdes[i].c;
        if(a!=b)
        {
            p[a]=b;
            sum+=c;
            add(egdes[i].a,egdes[i].b,c),add(egdes[i].b,egdes[i].a,c);
            egdes[i].flag=true;
        }
    }
    for(int i=1;i<=n;i++) dfs(i,-1,-1,-1,dis1[i],dis2[i]);
    ll ans=1e18;
    for(int i=0;i<m;i++)
    {
        if(!egdes[i].flag)
        {
            int a=egdes[i].a,b=egdes[i].b,c=egdes[i].c;
            if(c>dis1[a][b]) ans=min(ans,sum+c-dis1[a][b]);
            else if(c>dis2[a][b]) ans=min(ans,sum+c-dis2[a][b]);
        }
    }
    cout <<ans<<endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章