「LOJ#10068」「一本通 3.1 练习 3」秘密的牛奶运输(次小生成树

题目描述

Farmer John 要把他的牛奶运输到各个销售点。运输过程中,可以先把牛奶运输到一些销售点,再由这些销售点分别运输到其他销售点。 运输的总距离越小,运输的成本也就越低。低成本的运输是 Farmer John 所希望的。不过,他并不想让他的竞争对手知道他具体的运输方案,所以他希望采用费用第二小的运输方案而不是最小的。现在请你帮忙找到该运输方案。

输入格式

第一行是两个整数 N,MN,MN,M,表示顶点数和边数;

接下来 MMM 行每行 333 个整数,x,y,zx,y,zx,y,z,表示一条路的两端 x,yx,yx,y 和距离 zzz

输出格式

仅一行,输出第二小方案。

样例

样例输入

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

样例输出

450

数据范围与提示

对于全部数据,1≤N≤500,1≤M≤104,1≤z≤1091le Nle 500,1le Mle 10^4,1le zle 10^91N500,1M104,1z109,数据可能有重边。

题解

这是一道裸的次小生成树。

次小生成树的两种食用方法:

  • 就叫它法一吧。
    • 首先求个最小生成树。
    • 然后每次删掉一条树边,求树边两端两点的最短路。
    • 时间复杂度$O(nmlogm)$
  • 就叫它法二吧。
    • 也是首先求个最小生成树。
    • 之后求树上每两点的路径间最大的边权,需要$O(n^2)$。(枚举每个点做根,然后$O(n)$遍历
    • 然后枚举每个非树边,设端点为$i,j$。
    • 那么如果把这条边接上生成树,那么就把它所在的环上边权最大的那条边断掉,就是接上这条边的最优解。
    • 所以枚举每条边,维护$最小生成树的权值和-MAX[i][j]+a[i][j]$的最小值。
    • 复杂度$O(n^2+m)$

然后这里写的是法二。

 1 编号    题目    状态    分数    总时间    内存    代码 / 答案文件    提交者    提交时间
 2 #240021    #10068. 「一本通 3.1 练习 3」秘密的牛奶运输 Accepted    100    74 ms    1404 KiB    C++ / 1.3 K    qwerta    2018-10-22 20:33:11
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cstdio>
 6 using namespace std;
 7 struct emm{
 8     int l,r,v,tag;
 9 }a[10003];
10 bool cmp(emm qaq,emm qwq){
11     return qaq.v<qwq.v;
12 }
13 int fa[503];
14 int fifa(int x)
15 {
16     if(fa[x]==x)return x;
17     return fa[x]=fifa(fa[x]);
18 }
19 struct ahh{
20     int e,f,v;
21 }b[1003];
22 int h[503];
23 int cnt=0;
24 void con(int x,int y,int len)//加边
25 {
26     b[++cnt].f=h[x];
27     h[x]=cnt;
28     b[cnt].e=y;
29     b[cnt].v=len;
30     b[++cnt].f=h[y];
31     h[y]=cnt;
32     b[cnt].e=x;
33     b[cnt].v=len;
34     return;
35 }
36 int MAX[503][503];//两点树上路径中边权最大值
37 int s;
38 void dfs(int x)//dfs找MAX[s][x]
39 {
40     for(int i=h[x];i;i=b[i].f)
41     if(!MAX[s][b[i].e]&&b[i].e!=s)
42     {
43         MAX[s][b[i].e]=max(MAX[s][x],b[i].v);
44         dfs(b[i].e);
45     }
46     return;
47 }
48 int main()
49 {
50     //freopen("a.in","r",stdin);
51     int n,m;
52     scanf("%d%d",&n,&m);
53     for(int i=1;i<=m;++i)
54     {
55         int x,y,z;
56         scanf("%d%d%d",&x,&y,&z);
57         a[i]=(emm){x,y,z};//建原图
58     }
59         //跑最小生成树
60     for(int i=1;i<=n;++i)
61       fa[i]=i;
62     sort(a+1,a+m+1,cmp);
63     int k=n-1,i=0;
64     long long now=0;//记录最小生成树的边权和
65     while(k)
66     {
67         ++i;
68         int u=fifa(a[i].l),v=fifa(a[i].r);
69         if(u!=v)
70         {
71             fa[u]=v;
72             con(a[i].l,a[i].r,a[i].v);//建树图
73             k--;
74             now+=a[i].v;
75             a[i].tag=1;//标记为树边
76         }
77     }
78     for(s=1;s<=n;++s)
79     {
80         dfs(s);
81     }
82     long long ans=1e15;
83     for(int i=1;i<=m;++i)
84     if(!a[i].tag&&a[i].v>MAX[a[i].l][a[i].r])//如果该边为非树边并且它的权值大于左右端点的树上距离(因为是次小生成树,所以权值要比最小生成树大
85     {
86         ans=min(ans,now-MAX[a[i].l][a[i].r]+a[i].v);//把路径上最大的换成这条边的答案
87     }
88     cout<<ans;
89     return 0;
90 }

题目描述

Farmer John 要把他的牛奶运输到各个销售点。运输过程中,可以先把牛奶运输到一些销售点,再由这些销售点分别运输到其他销售点。 运输的总距离越小,运输的成本也就越低。低成本的运输是 Farmer John 所希望的。不过,他并不想让他的竞争对手知道他具体的运输方案,所以他希望采用费用第二小的运输方案而不是最小的。现在请你帮忙找到该运输方案。

输入格式

第一行是两个整数 N,MN,MN,M,表示顶点数和边数;

接下来 MMM 行每行 333 个整数,x,y,zx,y,zx,y,z,表示一条路的两端 x,yx,yx,y 和距离 zzz

输出格式

仅一行,输出第二小方案。

样例

样例输入

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

样例输出

450

数据范围与提示

对于全部数据,1≤N≤500,1≤M≤104,1≤z≤1091le Nle 500,1le Mle 10^4,1le zle 10^91N500,1M104,1z109,数据可能有重边。

原文地址:https://www.cnblogs.com/qwerta/p/9833283.html