UVA 11733 最小生成深林

---恢复内容开始---

一、题目描述:给定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+wt1+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\n",cas,tc,co);
66 
67     }
68     return 0;
69 }

 

---恢复内容结束---

posted @ 2013-11-13 23:32  little_w  阅读(331)  评论(0编辑  收藏  举报