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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章