【最小生成树杂题】

  • 这里谈一下最小生成树
  • 生成树的概念:连通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树。生成树是连通图的极小连通子图。所谓极小是指:若在树中任意增加一条边,则将出现一个回路;若去掉一条边,将会使之变成非连通图。 生成树各边的权值总和称为生成树的权。权最小的生成树称为最小生成树。
  • 最小生成树一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。常用于求最小生成树得算法包括kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法。
  • 最常用的是kruscal算法。kruscal算法的实质是用贪心策略来求得图的最小生成树。初始状态图中一条边也没有,每个点独立的存在于集合中;每次取出当前边权最小的边,判断能否将此边加入生成树中,判断依据:边的两个端点是否在一个集合中。若两点在一个集合中,则不需加入该边;若两点不在一个集合中,则将两点所在的集合合并,即集合中的点可以互相到达,无需两两之间再建边;如此做直到集合中各个点都联通或边全部枚举完毕;若此图联通,则为最小生成树。保证边权递增可以用快速排序的思想,维护集合关系可以用并查集的思想。排序复杂度O(eloge),枚举复杂度O(e),总体复杂度O(eloge)。
  • 相比于kruscal算法,prim算法比较复杂;同样的是,prim算法也使用了贪心的策略,不过该算法是由点来贪心的。初始时边集u为空,点集v有一个点x,x为任意一点。操作步骤:在所有点中找到一点y,使d[y,r]最小,其中r∈v;把该边加入u,改点加入v,更新答案。如此做直到每个点都在点集中或某些点无法加入点集。若所有点都在点集中,则为最小生成树。枚举每个点加入集合,复杂度O(n),枚举该点与点集的距离,复杂度O(n),总体复杂度O(n2)。若用堆优化枚举距离的过程,复杂度降至O(nlogn)。
  • 下面是关于最小生成树的几道杂题。

Problem 1 最小生成树

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz

输入输出格式

输入格式:

第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)

接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi

输出格式:

输出包含一个数,即最小生成树的各边的长度之和;如果该图不连通则输出orz

输入输出样例

输入样例#1:

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

输出样例#1:

7

说明

时空限制:1000ms,128M

数据规模:

对于20%的数据:N<=5,M<=20

对于40%的数据:N<=50,M<=2500

对于70%的数据:N<=500,M<=10000

对于100%的数据:N<=5000,M<=200000

样例解释:

所以最小生成树的总边权为2+2+3=7

  • 最小生成树模板题,由数据范围可知,kruscal算法即可。复杂度O(mlogm)。
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 using namespace std;
 5
 6 struct bian {
 7     int x,y,a;
 8 } e[200050];
 9
10 bool cmp(const bian p,const bian q){
11     return p.a<q.a;
12 }
13
14 int n,m,ans,t,father[5050];
15
16 int gf(int x) {
17     if (father[x]==x) return(x);
18     father[x]=gf(father[x]);
19     return(father[x]);
20 }
21
22 int main(){
23     scanf("%d%d",&n,&m);
24     for (int i=1; i<=m; i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].a);
25     for (int i=1; i<=n; i++) father[i]=i;
26     sort(e+1,e+m+1,cmp);
27     for (int i=1; i<=m; i++) {
28         int fx=gf(e[i].x);
29         int fy=gf(e[i].y);
30         if (fx!=fy) {
31             father[fx]=fy;
32             ans+=e[i].a;
33         }
34     }
35     t=gf(1);
36     bool f=true;
37     for (int i=2; i<=n; i++) {
38         if (gf(i)!=t) {
39             printf("orz");
40             f=0;
41             break;
42         }
43     }
44     if (f) printf("%d",ans);
45     return 0;
46 }

Problem 2 公路修建

题目描述

某国有n个城市,它们互相之间没有公路相通,因此交通十分不便。为解决这一“行路难”的问题,政府决定修建公路。修建公路的任务由各城市共同完成。

修建工程分若干轮完成。在每一轮中,每个城市选择一个与它最近的城市,申请修建通往该城市的公路。政府负责审批这些申请以决定是否同意修建。

政府审批的规则如下:

(1)如果两个或以上城市申请修建同一条公路,则让它们共同修建;

(2)如果三个或以上的城市申请修建的公路成环。如下图,A申请修建公路AB,B申请修建公路BC,C申请修建公路CA。则政府将否决其中最短的一条公路的修建申请;

(3)其他情况的申请一律同意。

一轮修建结束后,可能会有若干城市可以通过公路直接或间接相连。这些可以互相:连通的城市即组成“城市联盟”。在下一轮修建中,每个“城市联盟”将被看作一个城市,发挥一个城市的作用。

当所有城市被组合成一个“城市联盟”时,修建工程也就完成了。

你的任务是根据城市的分布和前面讲到的规则,计算出将要修建的公路总长度。

输入输出格式

输入格式:

第一行一个整数n,表示城市的数量。(n≤5000)

以下n行,每行两个整数x和y,表示一个城市的坐标。(-1000000≤x,y≤1000000)

输出格式:

一个实数,四舍五入保留两位小数,表示公路总长。(保证有惟一解)

输入输出样例

输入样例#1:

4
0 0
1 2
-1 2
0 4

输出样例#1:

6.47

说明

修建的公路如图所示:

  • 本题思路比较明确,即求出最小生成树即可。
  • 数据范围,n≤5000。相当于5000*5000条边(kruscal已gg),那只能用prim了。
  • 复杂度O(n2),应该是能通过的。
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cmath>
 5
 6 int n,x[5050],y[5050];
 7 double d[5050],ans;
 8 bool v[5050];
 9
10 double cal(int a,int b)
11 {
12      double e1,e2;
13      e1=abs(x[a]-x[b]);e2=abs(y[a]-y[b]);
14      return sqrt(e1*e1+e2*e2);
15 }
16
17 int main() {
18     scanf("%d",&n);
19     for (int i=1; i<=n; i++) scanf("%d%d",&x[i],&y[i]);
20     for (int i=2; i<=n; i++) d[i]=cal(1,i);
21     d[1]=0;
22     v[1]=1;
23     for (int i=2; i<=n; i++) {
24         double minn=2147483647;
25         int cur=0;
26         for (int j=1; j<=n; j++)
27             if (!v[j] && d[j]<minn) {
28                 minn=d[j];
29                 cur=j;
30             }
31         ans=ans+d[cur];
32         v[cur]=1;
33         for (int j=1; j<=n; j++) if (!v[j] && d[j]>cal(cur,j)) d[j]=cal(cur,j);
34     }
35     printf("%.2lf",ans);
36     return 0;
37 }

Problem 3 清理仓库

题目描述

李老师的仓库已经有很多年没有清扫了,所以这次的计划是用河水来冲。仓库是一个N*M的矩形,且每个格子里都堆满了尘土。相邻的格子之间都有门,要想让水冲进去,就必须打开这些门。这可不是一件容易的事情。因为有些格子里土堆得很高,因此打开门就很费劲。推开(或拉开)一扇从A格子到B格子的门,需要的力度值为B房间里土堆的高度。写一个程序计算至少需要花费多少力气,才能使所有的格子都进水。

输入输出格式

输入格式:

第一行为N和M(N, M <= 40),代表仓库的大小。

以后N行,每行N个整数(每个数不超过100),分别表示每个格子里土堆的厚度。

输出格式:

你得到的结果。所有的格子必须都进水。水是从左上角的格子进去的。

输入输出样例

输入样例#1:

3 4
3 5 2 1
7 3 4 8
1 6 5 7

输出样例#1:

26
  • 一道最小生成树建模的题。由题意,若要两个房间联通,需要打开中间的门,而开门只有两种方式,即推开和拉开,据此建边,求最小生成树即可。
  • 时间复杂度O(n*m*4*log (n*m*4)),可以通过该题。
 1 var
 2     n,m,i,j,k,fx,fy,ans        :longint;
 3     father                    :array[0..2050] of longint;
 4     a                        :array[0..2050] of longint;
 5     l,r,w                    :array[0..10050] of longint;
 6
 7 procedure swap(var x,y:longint);
 8 var
 9     t                        :longint;
10 begin
11     t:=x;
12     x:=y;
13     y:=t;
14 end;
15
16 procedure sort(ll,rr:longint);
17 var
18     i,j,x                    :longint;
19 begin
20     i:=ll; j:=rr; x:=w[(ll+rr)>>1];
21     while i<j do
22     begin
23         while w[i]<x do inc(i);
24         while w[j]>x do dec(j);
25         if i<=j then
26         begin
27             swap(w[i],w[j]);
28             swap(l[i],l[j]);
29             swap(r[i],r[j]);
30             inc(i);
31             dec(j);
32         end;
33     end;
34     if i<rr then sort(i,rr);
35     if j>ll then sort(ll,j);
36 end;
37
38 function min(x,y:longint):longint;
39 begin
40     if x<y then exit(x) else exit(y);
41 end;
42
43 function gf(x:longint):longint;
44 begin
45     if father[x]=x then exit(x);
46     father[x]:=gf(father[x]);
47     exit(father[x]);
48 end;
49
50 begin
51     read(n,m);
52     for i:=1 to n*m do read(a[i]);
53     for i:=1 to n*m do father[i]:=i;
54     for i:=1 to n*m do
55     begin
56         if i mod m<>0 then
57         begin
58             inc(k);
59             l[k]:=i;
60             r[k]:=i+1;
61             w[k]:=min(a[l[k]],a[r[k]]);
62         end;
63         if i<=(n-1)*m then
64         begin
65             inc(k);
66             l[k]:=i;
67             r[k]:=i+m;
68             w[k]:=min(a[l[k]],a[r[k]]);
69         end;
70     end;
71     sort(1,k);
72     for i:=1 to k do
73     begin
74         fx:=gf(l[i]);
75         fy:=gf(r[i]);
76         if fx<>fy then
77         begin
78             father[fx]:=fy;
79             inc(ans,w[i]);
80         end;
81     end;
82     writeln(ans);
83 end.

Problem 4 [NOIP2013] 货车运输

题目描述

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入输出格式

输入格式:

输入文件名为 truck.in。

输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道

路。 接下来 m 行每行 3 个整数 x、 y、 z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。意:x 不等于 y,两座城市之间可能有多条道路。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

输出格式:

输出文件名为 truck.out。

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货

车不能到达目的地,输出-1。

输入输出样例

输入样例#1:

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

输出样例#1:

3
-1
3

说明

对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000; 对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000; 对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。

 

  • 由题意可知本题要求图中联通边尽量大,所以可以想到最大生成树。
  • 首先可以用kruscal算法得到一颗最大生成树,接着要找出两点间连边最小边权的边。比较简单的想法:暴力的求出路径上边的最小值,但复杂度较高,不是理想的策略。我们可以用倍增的想法,维护两点的最近公共祖先(lca)以及它们到最近公共祖先之间的距离。该算法复杂度O(mlogm+qlogn),比较理想,可以通过本题。
  1 type
  2     rec                    =record
  3     l,r,w                :longint;
  4 end;
  5
  6 var
  7     n,m,i,j,x,y,z,q,fx,fy,root,l    :longint;
  8     e                                :array[0..100050] of rec;
  9     pre,last,other,len,d            :array[0..100050] of longint;
 10     vis                                :Array[0..100050] of boolean;
 11     father                            :array[0..100050] of longint;
 12     anc                                :array[0..100050,0..30] of longint;
 13     minn                            :Array[0..100050,0..30] of longint;
 14
 15 function min(x,y:longint):longint;
 16 begin
 17     if x<y then exit(x) else exit(y);
 18 end;
 19
 20 procedure swap(var a,b:rec);
 21 var
 22     c                                :rec;
 23 begin
 24     c:=a;
 25     a:=b;
 26     b:=c;
 27 end;
 28
 29 procedure sort(l,r:longint);
 30 var
 31     i,j,x                            :longint;
 32 begin
 33     i:=l; j:=r; x:=e[(l+r)>>1].w;
 34     while i<j do
 35     begin
 36         while e[i].w<x do inc(i);
 37         while e[j].w>x do dec(j);
 38         if i<=j then
 39         begin
 40             swap(e[i],e[j]);
 41             inc(i);
 42             dec(j);
 43         end;
 44     end;
 45     if i<r then sort(i,r);
 46     if j>l then sort(l,j);
 47 end;
 48
 49 function gf(x:longint):longint;
 50 begin
 51     if father[x]=x then exit(x);
 52     father[x]:=gf(father[x]);
 53     exit(father[x]);
 54 end;
 55
 56 procedure add(u,v,r:longint);
 57 begin
 58     inc(l);
 59     pre[l]:=last[u];
 60     last[u]:=l;
 61     other[l]:=v;
 62     len[l]:=r;
 63 end;
 64
 65 procedure dfs(x:longint);
 66 var
 67     p,q                                :longint;
 68 begin
 69     p:=last[x];
 70     while p<>0 do
 71     begin
 72         q:=other[p];
 73         if (not vis[q]) then
 74         begin
 75             vis[q]:=true;
 76             d[q]:=d[x]+1;
 77             minn[q,0]:=len[p];
 78             anc[q,0]:=x;
 79             dfs(q);
 80         end;
 81         p:=pre[p];
 82     end;
 83 end;
 84
 85 procedure bz;
 86 var
 87     i,j                                :longint;
 88 begin
 89     for j:=1 to 25 do
 90         for i:=1 to n do
 91         begin
 92             minn[i,j]:=min(minn[i,j-1],minn[anc[i,j-1],j-1]);
 93             anc[i,j]:=anc[anc[i,j-1],j-1];
 94         end;
 95 end;
 96
 97 function lca(u,v:longint):longint;
 98 var
 99     t,ans,i                            :longint;
100 begin
101     if d[u]<d[v] then
102     begin
103         t:=u;
104         u:=v;
105         v:=t;
106     end;
107     ans:=maxlongint;
108     for i:=25 downto 0 do
109     begin
110         if d[anc[u,i]]>=d[v] then
111         begin
112             ans:=min(ans,minn[u,i]);
113             u:=anc[u,i];
114         end;
115     end;
116     if u=v then exit(ans);
117     for i:=25 downto 0 do
118     begin
119         if (anc[u,i]<>anc[v,i]) then
120         begin
121             ans:=min(ans,minn[u,i]);
122             ans:=min(ans,minn[v,i]);
123             u:=anc[u,i];
124             v:=anc[v,i];
125         end;
126     end;
127     ans:=min(ans,minn[u,0]);
128     ans:=min(ans,minn[v,0]);
129     exit(ans);
130 end;
131
132 begin
133     read(n,m);
134     for i:=1 to n do father[i]:=i;
135     for i:=1 to m do read(e[i].l,e[i].r,e[i].w);
136     sort(1,m);
137     for i:=m downto 1 do
138     begin
139         fx:=gf(e[i].l);
140         fy:=gf(e[i].r);
141         if (fx<>fy) then
142         begin
143             father[fx]:=fy;
144             add(e[i].l,e[i].r,e[i].w);
145             add(e[i].r,e[i].l,e[i].w);
146             root:=e[i].l;
147         end;
148     end;
149     d[root]:=1;
150     vis[root]:=true;
151     dfs(root);
152     bz;
153     {writeln(root);
154     for i:=1 to n do
155     begin
156         for j:=0 to 10 do write(anc[i,j]);
157         writeln;
158     end;
159     for i:=1 to n do writeln(d[i]);}
160     read(q);
161     for i:=1 to q do
162     begin
163         read(x,y);
164         if gf(x)<>gf(y) then writeln(-1) else writeln(lca(x,y));
165     end;
166 end.
时间: 2024-12-24 03:08:14

【最小生成树杂题】的相关文章

_杂题_

杂题集 是个放题的好地方! **** 5.28 **** - BZOJ [3052] 糖果公园 - 据说是一道区间操作的综合题,但现在貌似蹦了? 现在还是太水,之后再来写吧. *************

[杂题]URAL1822. Hugo II&#39;s War

看懂题意的请直接跳过下一坨! 本人有表达障碍! ========================================== 题意: (题意真的很难很难懂啊!!!  去他娘的**) 有一个王国,王国里有一个国王(编号为1),他有(编号为2~n) n-1个臣子(这些臣子并不全和他有直接关系) 然后呢 国王要去打架,但是只有当他的x%个及以上的直系下属(与他有直接关系的臣子)做好打架的准备了,他才能去打架 他的直系下属也有下属,也要其中x%及以上的下属做好打架准备了,那些直系下属才会开始准备

hdu 1162 Eddy&#39;s picture 最小生成树入门题 Prim+Kruskal两种算法AC

Eddy's picture Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7428    Accepted Submission(s): 3770 Problem Description Eddy begins to like painting pictures recently ,he is sure of himself to

hdu 3641 数论 二分求符合条件的最小值数学杂题

http://acm.hdu.edu.cn/showproblem.php?pid=3641 学到: 1.二分求符合条件的最小值 /*==================================================== 二分查找符合条件的最小值 ======================================================*/ ll solve() { __int64 low = 0, high = INF, mid ; while(low <=

hdu 4961 数学杂题

http://acm.hdu.edu.cn/showproblem.php?pid=4961 先贴个O(nsqrtn)求1-n所有数的所有约数的代码: vector<int>divs[MAXN]; void caldivs() { for(int i=1;i<MAXN;i++) for(int j=i;j<MAXN;j+=i) divs[j].push_back(i); } 有了这个当时理下思路就可写了,但是重复数处理注意: 1.用一个数组vis[]  vis[i]=1表示i存在

poj 杂题 - 1959 Darts

这一题放在杂题里,是因为我没有用DP,而是使用的枚举,当然是受到了discuss里面的启发. 因为我们只能有三次机会,每一次只可以是固定的63个数,所以枚举感觉更加直观,但是不知道是不是没有DP快. #include<stdio.h> #include<string.h> int n; int Darts[63]; int main(){ int t,c=1,i,j,k,res; scanf("%d",&t); for(i = 0 ;i<=20;i

poj 1258 Agri-Net(最小生成树果题)

题目链接:http://poj.org/problem?id=1258 Description Farmer John has been elected mayor of his town! One of his campaign promises was to bring internet connectivity to all farms in the area. He needs your help, of course. Farmer John ordered a high speed

HDU 1233 还是畅通工程【最小生成树入门题,Kruskal算法+Prim算法】

还是畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 39929    Accepted Submission(s): 18144 Problem Description 某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离.省政府"畅通工程"的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路

poj 杂题 - 2081 Recaman&#39;s Sequence

这道题目一开始就能知道考点在如何缩短查找时间.所以加快查找是我们的重点.但是在大数据面前,查找算法都不够快,所以我们用简单的hash思想来做. 我们开一个数组a,当出现了一个数b时,把该数作为下标调整值,即a[b] = -1,下一次出现该值的时候直接去找这个值作为下标的a值是否为-1即可. #include<stdio.h> #include<string.h> #define MAX 5000010 int p[MAX]={0}; int s[MAX]={0}; int main