题目描述
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≤1091\le N\le 500,1\le M\le 10^4,1\le z\le 10^91≤N≤500,1≤M≤104,1≤z≤109,数据可能有重边。
题解
这是一道裸的次小生成树。
次小生成树的两种食用方法:
- 就叫它法一吧。
- 首先求个最小生成树。
- 然后每次删掉一条树边,求树边两端两点的最短路。
- 时间复杂度$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≤1091\le N\le 500,1\le M\le 10^4,1\le z\le 10^91≤N≤500,1≤M≤104,1≤z≤109,数据可能有重边。