---恢复内容开始---
一、题目描述:给定n(<=10000)个点,m(<=100000)条边,给出修每条边的花费f[i],可在某些点上修机场,给出修机场花费w。在一个连通分量里,只要有一个机场,则所有点都能达到机场。求最小修机场和修路的最小花费和。
二、策略:贪心+证明
三、具体算法和证明:
1、因为这道题本身和构建连通分量相关,所以我先向最小生成树考虑。
2、我们对所有的边按照花费f从小到大排序,假设这样一种情况:
f1<=f2<=f3<=f4<=f5<=f6.........<=fi<=.........fn
3、假设从fi开始往后f>w。对于前面的边,首先拿出第一条边f1建边,两点选一个建造机场;对于后续读取的边,如果两个顶点在同一联通分量里,显然这条边不需要修建。关键是下面两种情况:
(1)红色是当前读取到的边,那么我们发现在7节点单独建一个新的飞机场显然更划算;我们可以用归纳证明得,当读取到后续的边,只要f>w时,在两个节点上分别建飞机场是最优的(比建一个飞机场和一条边,或者在一个联通分量中建几个机场和几条边(见右上图)都要更优)
(2)遇到一个新的联通分量,简单类推,那么和上述的建边过程相同的。
四、算法实现:
f小于w的部分(最小生成树算边权和count+并查集判联通分量t1);f大于w的个数t2
答案为count+w(t1+t2)
我们可以发现上面的公式,利用并查集本身特点是是可合并,所以就是P[x]==[x]判总的联通分量即可。
五、代码实现:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int p[11000]; 5 struct edge 6 { 7 int u; 8 int v; 9 int f; 10 edge() {} 11 edge(int uu,int vv,int ff) 12 { 13 u=uu; 14 v=vv; 15 f=ff; 16 } 17 } e[111000]; 18 int find(int x) 19 { 20 return p[x] != x ? p[x]=find(p[x]) : x; 21 } 22 int cmp(edge a,edge b) 23 { 24 return a.f<b.f; 25 } 26 int main() 27 { 28 int t; 29 scanf("%d",&t); 30 for(int cas=1; cas<=t; cas++) 31 { 32 int n,m,w; 33 int cnt=0; 34 scanf("%d%d%d",&n,&m,&w); 35 for(int i=1;i<=n;i++) 36 p[i]=i; 37 for(int i=1; i<=m; i++) 38 { 39 int u; 40 int v; 41 int f; 42 scanf("%d%d%d",&u,&v,&f); 43 e[cnt++]=edge(u,v,f); 44 } 45 sort(e,e+cnt,cmp); 46 int p1=m; 47 for(int i=0;i<m;i++) 48 if (e[i].f>=w) {p1=i;break;} 49 long long tc=0; 50 for(int i=0;i<p1;i++)//从p1开始,以后f>w 51 { 52 int pu=find(e[i].u); 53 int pv=find(e[i].v); 54 55 if (pu==pv) continue; 56 tc+=e[i].f;//建边的总花费 57 p[pu]=pv; 58 } 59 int co=0; 60 for(int i=1;i<=n;i++) 61 { 62 if (i==p[i]) co++;//判连通分量的个数 63 } 64 tc+=co*w; 65 printf("Case #%d: %lld %d ",cas,tc,co); 66 67 } 68 return 0; 69 }
---恢复内容结束---