解题思路:
该题让求图的最小生成树是无疑的。不过对最小生成树进行了变形。
首先两点间可以通过修路,也可通过建码头来建立联系。就好像航电的引水工程一样,一个点要想喝水,
可以自己打井,也可以从别处饮水。当然和这个题目有所不同,引水工程是自己打井可满足自己也可满足
别人,而该题,只有建港的人,才可相互到达,并不是一地建港就可到达他人的。根据引水工程的思路,
同样可以可虑把河看成节点0.则可建港口的点都要像节点0引一条边。
1.对于权值为负数的边,可以挣钱,有多少条,修多少点,这是无疑的。
2.单纯进行一次库鲁斯卡尔算法是不行的,起初考虑加上建港求一次最小生成树。但是有可能某个点
建立港口费用特别小,则这条边会被入选,但其他点建立港口费用又特别大,但是被选的点已经建港,
我们在修路,无疑是多耗费花费,因为尽量不要又建港又修路的,由于该点建港,可能其他点必须建港
与其相连,造成答案增大。
因此求两边最小生成树,第一次不考虑建港,对于这种情况万一不存在最小生成树,则第一个答案我们
可以设为无穷大,第二次我们加上考虑建港再来一次最小生成树,这次答案是肯定存在,因为题目说了
这种情形答案一定存在,取这两次的最小值就是答案。
注意事项:
参考代码:
#include <iostream> #include <stdio.h> #include <algorithm> #include <vector> #include <string.h> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn = 10010; //节点的数目 const int maxm = 120000; //边的数目 int n,m; int f[maxn]; struct Edge{ int u,v; ll w; }edge[maxm]; bool cmp(Edge e1,Edge e2) { return e1.w < e2.w; } void initSet() { for(int i = 0; i <= n; i++) { f[i] = i; } } int findSet(int x) { int tmp=x; while(x != f[x]) { x = f[x]; } while(tmp != f[tmp]) { tmp = f[tmp]; f[tmp] = x; } return x; } void unionSet(int x,int y) { f[x] = y; } //不考虑建码头。 ll kruskal(int flag) { initSet(); ll ans = 0; int i = 0; int a,b,fa,fb; while(i < m) { //不考虑建码头的情况 a = edge[i].u; b = edge[i].v; if(flag && a==0) { i++; continue; } fa = findSet(a); fb = findSet(b); if(fa != fb) { unionSet(fa,fb); ans += edge[i].w; } else if(edge[i].w <= 0) { ans += edge[i].w; } i++; } return ans; } int main() { while(~scanf("%d%d",&n,&m)) { for(int i = 0; i < m; i++) { scanf("%d%d%lld",&edge[i].u,&edge[i].v,&edge[i].w); } ll x; //用点0代表河。 for(int i = 1; i <= n; i++) { scanf("%lld",&x); if(x != -1) { edge[m].u = 0; edge[m].v = i; edge[m++].w = x; } } sort(edge,edge+m,cmp); ll ans1,ans2; //不考虑建码头的情况 ans1 = kruskal(1); int cnt = 0; for(int i = 1; i <= n; i++) { if(f[i]==i) cnt++; } //光考虑建路,没有最小生成树。 if(cnt > 1) { ans1 = (ll)1000000000; } //同时考虑建码头的情况。 ans2 = kruskal(0); printf("%lld\n",min(ans1,ans2)); } return 0; }
0.0分
1 人评分
点我有惊喜!你懂得!浏览:2248 |
C语言程序设计教程(第三版)课后习题10.2 (C语言代码)浏览:1055 |
C语言程序设计教程(第三版)课后习题4.9 (C语言代码)浏览:717 |
简单的a+b (C语言代码)浏览:528 |
时间转换 (Java代码)浏览:617 |
九宫重排 (C++代码)浏览:2194 |
C语言程序设计教程(第三版)课后习题10.1 (Java代码)浏览:1492 |
C语言程序设计教程(第三版)课后习题1.5 (C语言代码)浏览:512 |
WU-链表数据求和操作 (C++代码)浏览:1382 |
printf基础练习2 (C语言代码)浏览:690 |