生成树专题

1、POJ 1251 Jungle Roads

  题意:给出个村庄之间的道路及其维修费;从中选取一些路使道路的维修费最小且保证各村庄之间道路畅通。

  思路:最小生成树模板。

①Kruskal

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 int n;
 6 struct side
 7 {
 8     int v1;
 9     int v2;
10     int len;
11     side(int vv1=0,int vv2=0,int ll=0):v1(vv1),v2(vv2),len(ll){}
12     friend bool operator <(const side&a,const side&b)
13     {
14         return a.len > b.len;
15     }
16 };
17 vector<side>minHeap;
18 const int maxn = 30;
19 int pre[maxn];
20 int Find(int x)
21 {
22     int r = x;
23     while (r != pre[r])
24     {
25         r = pre[r];
26     }
27     int c = x, p;
28     while (c != r)
29     {
30         p = pre[c];
31         pre[c] = r;
32         c = p;
33     }
34     return r;
35 }
36 void Join(int x, int y)
37 {
38     int f1 = Find(x), f2 = Find(y);
39     if (f1 != f2) pre[f1] = f2;
40 }
41 int Kruskal()
42 {
43     side tmp;
44     int cnt = 1;
45     for (int i = 0; i <= n; i++)pre[i] = i;
46     int ans = 0;
47     while (cnt < n)
48     {
49         pop_heap(minHeap.begin(), minHeap.end());
50         tmp = minHeap.back();
51         minHeap.pop_back();
52         int u = Find(tmp.v1);
53         int v = Find(tmp.v2);
54         if (u != v)
55         {
56             Join(tmp.v1, tmp.v2);
57             ans += tmp.len;
58             cnt++;
59         }
60     }
61     return ans;
62 }
63 
64 int main()
65 {
66     while (~scanf("%d", &n))
67     {
68         if (n == 0)break;
69         minHeap.clear();
70         char v1, v2;
71         int len;
72         int k;
73         for (int i = 0; i < n - 1; i++)
74         {
75 
76             cin >> v1;
77             scanf("%d", &k);
78             for (int j = 0; j < k; j++)
79             {
80                 cin >> v2;
81                 scanf("%d", &len);
82                 minHeap.push_back(side(v1 - 'A', v2 - 'A', len));
83             }
84         }
85         make_heap(minHeap.begin(), minHeap.end());
86         int ans=Kruskal();
87         printf("%d\n", ans);
88     }
89     return 0;
90 }
View Code

②Prime

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 int n;
 6 const int maxn = 30;
 7 struct node
 8 {
 9     int to;
10     int len;
11     node(int tt=0,int ll=0):to(tt),len(ll){ }
12     friend bool operator <(const node&a, const node&b)
13     {
14         return a.len > b.len;
15     }
16 };
17 vector<node>mp[maxn];
18 vector<node>minheap;
19 bool vis[maxn];
20 bool Cmp(int a, int b)
21 {
22     return a > b;
23 }
24 int Prime()
25 {
26     memset(vis, 0, sizeof(vis));
27     minheap.clear();
28     int cnt = 1;
29     vis[0] = true;
30     int sz = mp[0].size();
31     for (int i = 0; i < sz; i++)
32     {
33         minheap.push_back(mp[0][i]);
34     }
35     make_heap(minheap.begin(), minheap.end());
36     node tmp;
37     int ans = 0;
38     while (cnt < n)
39     {
40         do
41         {
42             pop_heap(minheap.begin(), minheap.end());
43             tmp = minheap.back();
44             minheap.pop_back();
45         } while (vis[tmp.to]);
46         ans += tmp.len;
47         cnt++;
48         int u = tmp.to;
49         vis[u] = true;
50         int sz = mp[u].size();
51         for (int i = 0; i < sz; i++)
52         {
53             if (vis[mp[u][i].to])continue;
54             minheap.push_back(mp[u][i]);
55             push_heap(minheap.begin(), minheap.end());
56         }
57 
58     }
59     return ans;
60 }
61 int main()
62 {
63     while (~scanf("%d", &n))
64     {
65         if (n == 0)break;
66         for (int i = 0; i <= n; i++) mp[i].clear();
67         char v1, v2;
68         int len;
69         int k;
70         for (int i = 0; i < n - 1; i++)
71         {
72 
73             cin >> v1;
74             scanf("%d", &k);
75             for (int j = 0; j < k; j++)
76             {
77                 cin >> v2;
78                 scanf("%d", &len);
79                 mp[v1 - 'A'].push_back(node(v2 - 'A', len));
80                 mp[v2 - 'A'].push_back(node(v1 - 'A', len));
81             }
82         }
83         int ans = Prime();
84         printf("%d\n", ans);
85     }
86     return 0;
87 }
View Code

2、POJ 1287 Networking

  题意:找出连接所有电脑的最短距离。

  思路:求最小生成树的总权值。

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 int n,m;
 6 const int maxn = 55;
 7 int pre[maxn];
 8 struct side
 9 {
10     int v1;
11     int v2;
12     int len;
13     side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll)
14     {
15     }
16     friend bool operator <(const side&a, const side&b)
17     {
18         return a.len > b.len;
19     }
20 };
21 vector<side>minHeap;
22 int Find(int x)
23 {
24     int r = x;
25     while (r != pre[r])
26     {
27         r = pre[r];
28     }
29     int c = x, p;
30     while (c != r)
31     {
32         p = pre[c];
33         pre[c] = r;
34         c = p;
35     }
36     return r;
37 }
38 void Join(int x, int y)
39 {
40     int f1 = Find(x), f2 = Find(y);
41     if (f1 != f2) pre[f1] = f2;
42 }
43 int Kruskal()
44 {
45     side tmp;
46     int cnt = 1;
47     for (int i = 0; i <= n; i++)pre[i] = i;
48     int ans = 0;
49     while (cnt < n)
50     {
51         pop_heap(minHeap.begin(), minHeap.end());
52         tmp = minHeap.back();
53         minHeap.pop_back();
54         int u = Find(tmp.v1);
55         int v = Find(tmp.v2);
56         if (u != v)
57         {
58             Join(tmp.v1, tmp.v2);
59             ans += tmp.len;
60             cnt++;
61         }
62     }
63     return ans;
64 }
65 int main()
66 {
67     while (~scanf("%d", &n))
68     {
69         if (n == 0)break;
70         minHeap.clear();
71         int v1, v2;
72         int len;
73         int k;
74         scanf("%d", &m);
75         for (int i = 0; i < m; i++)
76         {
77             scanf("%d%d%d", &v1, &v2, &len);
78             minHeap.push_back(side(v1, v2, len));
79         }
80         make_heap(minHeap.begin(), minHeap.end());
81         int ans = Kruskal();
82         printf("%d\n", ans);
83     }
84     return 0;
85 }
View Code

3、POJ 2031 Building a Space Station

  题意:空间站可以看成是圆球体,是建立在三维坐标系中的,给出N个空间站的(x,y,z)坐标及其半径r。如果两个空间站之间有接触(两球相交或相切),那么这两个空间站可以互相直接到达,否则(两球相离)需要在他们之间建立道路(道路长度为圆心距离减去两圆的半径)来连接。计算出将所有空间站连接起来所需要的最短路程。

  思路:以每个空间站为节点,它们之间的距离为边权,建立无向图,求最小生成树。

 

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 #include<cmath>
 5 using namespace std;
 6 int n;
 7 const int maxn = 105;
 8 int pre[maxn];
 9 struct side
10 {
11     int v1;
12     int v2;
13     double len;
14     side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll)
15     {
16     }
17     friend bool operator <(const side&a, const side&b)
18     {
19         return a.len > b.len;
20     }
21 };
22 vector<side>minHeap;
23 int Find(int x)
24 {
25     int r = x;
26     while (r != pre[r])
27     {
28         r = pre[r];
29     }
30     int c = x, p;
31     while (c != r)
32     {
33         p = pre[c];
34         pre[c] = r;
35         c = p;
36     }
37     return r;
38 }
39 void Join(int x, int y)
40 {
41     int f1 = Find(x), f2 = Find(y);
42     if (f1 != f2) pre[f1] = f2;
43 }
44 double Kruskal()
45 {
46     side tmp;
47     int cnt = 1;
48     for (int i = 0; i <= n; i++)pre[i] = i;
49     double ans = 0;
50     while (cnt < n)
51     {
52         pop_heap(minHeap.begin(), minHeap.end());
53         tmp = minHeap.back();
54         minHeap.pop_back();
55         int u = Find(tmp.v1);
56         int v = Find(tmp.v2);
57         if (u != v)
58         {
59             Join(tmp.v1, tmp.v2);
60             ans += tmp.len;
61             cnt++;
62         }
63     }
64     return ans;
65 }
66 struct node
67 {
68     double x;
69     double y;
70     double z;
71     double r;
72 }points[maxn];
73 int main()
74 {
75     while (~scanf("%d", &n))
76     {
77         if (n == 0)break;
78         minHeap.clear();
79         for (int i = 0; i < n; i++)
80         {
81             scanf("%lf%lf%lf%lf", &points[i].x, &points[i].y, &points[i].z,&points[i].r);
82         }
83         for (int i = 0; i < n; i++)
84         {
85             for (int j = i + 1; j < n; j++)
86             {
87                 double len = sqrt((points[i].x - points[j].x)*(points[i].x - points[j].x) + (points[i].y - points[j].y)*(points[i].y - points[j].y) + (points[i].z - points[j].z)*(points[i].z - points[j].z))-points[i].r-points[j].r;
88                 if (len < 0) len = 0;
89                 minHeap.push_back(side(i, j, len));
90             }
91         }
92         make_heap(minHeap.begin(), minHeap.end());
93         double ans = Kruskal();
94         printf("%.3lf\n", ans);
95     }
96     return 0;
97 }
View Code

 

4、hdu 1102 Constructing Roads

  题意:有n个村庄,编号1-n,以矩阵的形式给出任意两个村庄之间的距离,然后告诉已经有q个村庄已经修好了路,问现在要打算使所有村庄都联通需要修路的最小长度。

  思路:把已经修好路的村庄直接并。

 

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 #include<cmath>
 5 using namespace std;
 6 int n;
 7 const int maxn = 105;
 8 int pre[maxn];
 9 int mp[maxn][maxn];
10 struct side
11 {
12     int v1;
13     int v2;
14     double len;
15     side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll)
16     {
17     }
18     friend bool operator <(const side&a, const side&b)
19     {
20         return a.len > b.len;
21     }
22 };
23 vector<side>minHeap;
24 int Find(int x)
25 {
26     int r = x;
27     while (r != pre[r])
28     {
29         r = pre[r];
30     }
31     int c = x, p;
32     while (c != r)
33     {
34         p = pre[c];
35         pre[c] = r;
36         c = p;
37     }
38     return r;
39 }
40 bool Join(int x, int y)
41 {
42     int f1 = Find(x), f2 = Find(y);
43     if (f1 != f2)
44     {
45         pre[f1] = f2;
46         return false;
47     }
48     else return true;
49 }
50 double Kruskal(int cnt)
51 {
52     side tmp;
53     int ans = 0;
54     while (cnt < n)
55     {
56         pop_heap(minHeap.begin(), minHeap.end());
57         tmp = minHeap.back();
58         minHeap.pop_back();
59         int u = Find(tmp.v1);
60         int v = Find(tmp.v2);
61         if (u != v)
62         {
63             Join(tmp.v1, tmp.v2);
64             ans += tmp.len;
65             cnt++;
66         }
67     }
68     return ans;
69 }
70 int main()
71 {
72     while (~scanf("%d", &n))
73     {
74         minHeap.clear();
75         for (int i = 1; i <= n; i++)
76         {
77             for (int j = 1; j <= n; j++)
78             {
79                 scanf("%d", &mp[i][j]);
80                 if (mp[i][j] > 0) minHeap.push_back(side(i, j, mp[i][j]));
81             }
82         }
83         int q,cnt=1;
84         scanf("%d", &q);
85         for (int i = 0; i <= n; i++)pre[i] = i;
86         for (int i = 0; i < q; i++)
87         {
88             int u, v;
89             scanf("%d%d", &u, &v);
90             if (!Join(u, v)) cnt++;
91         }
92         make_heap(minHeap.begin(), minHeap.end());
93         int ans = Kruskal(cnt);
94         printf("%d\n", ans);
95     }
96     return 0;
97 }
View Code

 

5、zoj 1586 QS Network

  题意:首先给一个t,代表t个测试样例,再给一个n,表示有n个QS装置,接下来一行是n个QS装置的成本。接下来是n*n的矩阵,表示每两个QS 装置之间链接需要的费用。求在全联通的情况下求最少费用。

  思路:建立边时,边权值为两装置之间的连接费用+2装置的成本。

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 #include<cmath>
 5 using namespace std;
 6 int n;
 7 const int maxn = 1005;
 8 int pre[maxn];
 9 int adp[maxn];
10 int mp[maxn][maxn];
11 struct side
12 {
13     int v1;
14     int v2;
15     double len;
16     side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll)
17     {
18     }
19     friend bool operator <(const side&a, const side&b)
20     {
21         return a.len > b.len;
22     }
23 };
24 vector<side>minHeap;
25 int Find(int x)
26 {
27     int r = x;
28     while (r != pre[r])
29     {
30         r = pre[r];
31     }
32     int c = x, p;
33     while (c != r)
34     {
35         p = pre[c];
36         pre[c] = r;
37         c = p;
38     }
39     return r;
40 }
41 bool Join(int x, int y)
42 {
43     int f1 = Find(x), f2 = Find(y);
44     if (f1 != f2)
45     {
46         pre[f1] = f2;
47         return false;
48     }
49     else return true;
50 }
51 double Kruskal()
52 {
53     side tmp;
54     int cnt = 1;
55     int ans = 0;
56     for (int i = 0; i <= n; i++) pre[i] = i;
57     while (cnt < n)
58     {
59         pop_heap(minHeap.begin(), minHeap.end());
60         tmp = minHeap.back();
61         minHeap.pop_back();
62         int u = Find(tmp.v1);
63         int v = Find(tmp.v2);
64         if (u != v)
65         {
66             Join(tmp.v1, tmp.v2);
67             ans += tmp.len;
68             cnt++;
69         }
70     }
71     return ans;
72 }
73 int main()
74 {
75     int t;
76     scanf("%d", &t);
77     while (t--)
78     {
79         scanf("%d", &n);
80         minHeap.clear();
81         for (int i = 1; i <= n; i++) scanf("%d", &adp[i]);
82         for (int i = 1; i <= n; i++)
83         {
84             for (int j = 1; j <= n; j++)
85             {
86                 scanf("%d", &mp[i][j]);
87                 if (i!=j) minHeap.push_back(side(i, j, mp[i][j]+adp[i]+adp[j]));
88             }
89         }
90         make_heap(minHeap.begin(), minHeap.end());
91         int ans = Kruskal();
92         printf("%d\n", ans);
93     }
94     return 0;
95 }
View Code

6、poj 1789 Truck History

  题意:用一个7位的string代表一个编号,两个编号之间的distance代表这两个编号之间不同字母的个数。一个编号只能由另一个编号“衍生”出来,代价是这两个编号之间相应的distance,现在要找出一个“衍生”方案,使得总代价最小,也就是distance之和最小。

  思路:算出两两之间的dis,建边求最小生成树。

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 #include<cmath>
 5 using namespace std;
 6 int n;
 7 const int maxn = 2005;
 8 int pre[maxn];
 9 char mp[maxn][10];
10 struct side
11 {
12     int v1;
13     int v2;
14     double len;
15     side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll)
16     {
17     }
18     friend bool operator <(const side&a, const side&b)
19     {
20         return a.len > b.len;
21     }
22 };
23 vector<side>minHeap;
24 int Find(int x)
25 {
26     int r = x;
27     while (r != pre[r])
28     {
29         r = pre[r];
30     }
31     int c = x, p;
32     while (c != r)
33     {
34         p = pre[c];
35         pre[c] = r;
36         c = p;
37     }
38     return r;
39 }
40 bool Join(int x, int y)
41 {
42     int f1 = Find(x), f2 = Find(y);
43     if (f1 != f2)
44     {
45         pre[f1] = f2;
46         return false;
47     }
48     else return true;
49 }
50 double Kruskal()
51 {
52     side tmp;
53     int cnt = 1;
54     int ans = 0;
55     for (int i = 0; i <= n; i++) pre[i] = i;
56     while (cnt < n)
57     {
58         pop_heap(minHeap.begin(), minHeap.end());
59         tmp = minHeap.back();
60         minHeap.pop_back();
61         int u = Find(tmp.v1);
62         int v = Find(tmp.v2);
63         if (u != v)
64         {
65             Join(tmp.v1, tmp.v2);
66             ans += tmp.len;
67             cnt++;
68         }
69     }
70     return ans;
71 }
72 int main()
73 {
74     while (~scanf("%d", &n))
75     {
76         if (n == 0) break;
77         minHeap.clear();
78         for (int i = 1; i <= n; i++)
79         {
80             scanf("%s", mp[i]);
81         }
82         for (int i = 1; i <= n; i++)
83         {
84             for (int j = i + 1; j <= n; j++)
85             {
86                 int dis = 0;
87                 for (int pos = 0; pos < 7; pos++)
88                 {
89                     if (mp[i][pos] != mp[j][pos]) dis++;
90                 }
91                 minHeap.push_back(side(i, j, dis));
92             }
93         }
94         make_heap(minHeap.begin(), minHeap.end());
95         int ans = Kruskal();
96         printf("The highest possible quality is 1/%d.\n", ans);
97     }
98     return 0;
99 }
View Code

7、POJ 2349 Arctic Network

  题意:有S颗卫星和P个哨所,有卫星的两个哨所之间可以任意通信;否则,一个哨所只能和距离它小于等于D的哨所通信。给出卫星的数量和P个哨所的坐标,求D的最小值。

  思路:用最小生成树求前P-S-1条最小边,D则为第P-S-1边。

  1 #include<iostream>
  2 #include<vector>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<set>
  6 using namespace std;
  7 int n,s;
  8 const int maxn = 505;
  9 int pre[maxn];
 10 char mp[maxn][10];
 11 struct side
 12 {
 13     int v1;
 14     int v2;
 15     double len;
 16     side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll)
 17     {
 18     }
 19     friend bool operator <(const side&a, const side&b)
 20     {
 21         return a.len > b.len;
 22     }
 23 };
 24 vector<side>minHeap;
 25 struct node
 26 {
 27     int x;
 28     int y;
 29     node(int xx=0,int yy=0):x(xx),y(yy){ }
 30 }points[maxn];
 31 int Find(int x)
 32 {
 33     int r = x;
 34     while (r != pre[r])
 35     {
 36         r = pre[r];
 37     }
 38     int c = x, p;
 39     while (c != r)
 40     {
 41         p = pre[c];
 42         pre[c] = r;
 43         c = p;
 44     }
 45     return r;
 46 }
 47 bool Join(int x, int y)
 48 {
 49     int f1 = Find(x), f2 = Find(y);
 50     if (f1 != f2)
 51     {
 52         pre[f1] = f2;
 53         return false;
 54     }
 55     else return true;
 56 }
 57 double Kruskal()
 58 {
 59     side tmp;
 60     int cnt = s;
 61     for (int i = 0; i <= n; i++) pre[i] = i;
 62     double ans;
 63     while (cnt < n)
 64     {
 65         pop_heap(minHeap.begin(), minHeap.end());
 66         tmp = minHeap.back();
 67         minHeap.pop_back();
 68         int u = Find(tmp.v1);
 69         int v = Find(tmp.v2);
 70         if (u != v)
 71         {
 72             Join(tmp.v1, tmp.v2);
 73             ans = tmp.len;
 74             cnt++;
 75         }
 76     }
 77     return ans;
 78 }
 79 int main()
 80 {
 81     int t;
 82     scanf("%d", &t);
 83     while (t--)
 84     {
 85         scanf("%d%d",&s,&n);
 86         minHeap.clear();
 87         for (int i = 1; i <= n; i++)
 88         {
 89             scanf("%d%d",&points[i].x,&points[i].y);
 90         }
 91         for (int i = 1; i <= n; i++)
 92         {
 93             for (int j = i + 1; j <= n; j++)
 94             {
 95                 double dis = sqrt(1.0*(points[i].x-points[j].x)*(points[i].x - points[j].x)+ (points[i].y - points[j].y)*(points[i].y - points[j].y));
 96                 minHeap.push_back(side(i, j, dis));
 97             }
 98         }
 99         make_heap(minHeap.begin(), minHeap.end());
100         double re=Kruskal();
101         printf("%.2lf\n",re);
102     }
103     return 0;
104 }
View Code

 8、poj 1751 Highways

  题意:给你N个城市的坐标,城市之间存在公路,但是由于其中一些道路损坏了,需要维修,维修的费用与公路长成正比(公路是直的)。但现有M条公路是完整的,不需要维修,下面有M行,表示不需要维修的道路两端的城市,问最小费用。

  思路:同4.

 

  1 #include<iostream>
  2 #include<vector>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<set>
  6 using namespace std;
  7 int n;
  8 const int maxn =760;
  9 int pre[maxn];
 10 char mp[maxn][10];
 11 bool vis[maxn];
 12 struct side
 13 {
 14     int v1;
 15     int v2;
 16     double len;
 17     side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll)
 18     {
 19     }
 20     friend bool operator <(const side&a, const side&b)
 21     {
 22         return a.len > b.len;
 23     }
 24 };
 25 vector<side>minHeap;
 26 struct node
 27 {
 28     int x;
 29     int y;
 30     node(int xx = 0, int yy = 0) :x(xx), y(yy)
 31     {
 32     }
 33 }points[maxn];
 34 int Find(int x)
 35 {
 36     int r = x;
 37     while (r != pre[r])
 38     {
 39         r = pre[r];
 40     }
 41     int c = x, p;
 42     while (c != r)
 43     {
 44         p = pre[c];
 45         pre[c] = r;
 46         c = p;
 47     }
 48     return r;
 49 }
 50 bool Join(int x, int y)
 51 {
 52     int f1 = Find(x), f2 = Find(y);
 53     if (f1 != f2)
 54     {
 55         pre[f1] = f2;
 56         return false;
 57     }
 58     else return true;
 59 }
 60 double Kruskal(int cnt0)
 61 {
 62     side tmp;
 63     double ans=0;
 64     int cnt = cnt0;
 65     
 66     while (cnt < n)
 67     {
 68         pop_heap(minHeap.begin(), minHeap.end());
 69         tmp = minHeap.back();
 70         minHeap.pop_back();
 71         int u = Find(tmp.v1);
 72         int v = Find(tmp.v2);
 73         if (u != v)
 74         {
 75             Join(tmp.v1, tmp.v2);
 76             points[cnt - cnt0].x = tmp.v1, points[cnt - cnt0].y = tmp.v2;
 77             cnt++;
 78         }
 79     }
 80     return ans;
 81 }
 82 int main()
 83 {
 84     while (~scanf("%d",&n))
 85     {
 86         minHeap.clear();
 87         for (int i = 1; i <= n; i++)
 88         {
 89             scanf("%d%d", &points[i].x, &points[i].y);
 90         }
 91         for (int i = 1; i <= n; i++)
 92         {
 93             for (int j = i + 1; j <= n; j++)
 94             {
 95                 double dis = sqrt(1.0*(points[i].x - points[j].x)*(points[i].x - points[j].x) + (points[i].y - points[j].y)*(points[i].y - points[j].y));
 96                 minHeap.push_back(side(i, j, dis));
 97             }
 98         }
 99         for (int i = 0; i <= n; i++) pre[i] = i;
100         int m;
101         scanf("%d", &m);
102         memset(vis, 0, sizeof(vis));
103         int cnt = 1;
104         for (int i = 0; i < m; i++)
105         {
106             int u, v;
107             scanf("%d%d", &u, &v);
108             if (!Join(u, v)) cnt++;
109         }
110         make_heap(minHeap.begin(), minHeap.end());
111         Kruskal(cnt);
112         for (int i = 0; i < n - cnt; i++)
113         {
114             printf("%d %d\n", points[i].x, points[i].y);
115         }
116     }
117     return 0;
118 }
View Code

 

9、POJ 1258 Agri-Net

  题意:有n个农场,已知这n个农场都互相相通,有一定的距离,现在每个农场需要装光纤,问怎么安装光纤能将所有农场都连通起来,并且要使光纤距离最小,输出安装光纤的总距离。

  思路:最小生成树。

  1 #include<iostream>
  2 #include<vector>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<set>
  6 using namespace std;
  7 int n;
  8 const int maxn = 760;
  9 int pre[maxn];
 10 char mp[maxn][10];
 11 bool vis[maxn];
 12 struct side
 13 {
 14     int v1;
 15     int v2;
 16     int len;
 17     side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll)
 18     {
 19     }
 20     friend bool operator <(const side&a, const side&b)
 21     {
 22         return a.len > b.len;
 23     }
 24 };
 25 vector<side>minHeap;
 26 struct node
 27 {
 28     int x;
 29     int y;
 30     node(int xx = 0, int yy = 0) :x(xx), y(yy)
 31     {
 32     }
 33 }points[maxn];
 34 int Find(int x)
 35 {
 36     int r = x;
 37     while (r != pre[r])
 38     {
 39         r = pre[r];
 40     }
 41     int c = x, p;
 42     while (c != r)
 43     {
 44         p = pre[c];
 45         pre[c] = r;
 46         c = p;
 47     }
 48     return r;
 49 }
 50 bool Join(int x, int y)
 51 {
 52     int f1 = Find(x), f2 = Find(y);
 53     if (f1 != f2)
 54     {
 55         pre[f1] = f2;
 56         return false;
 57     }
 58     else return true;
 59 }
 60 int Kruskal(int cnt0)
 61 {
 62     side tmp;
 63     int ans = 0;
 64     int cnt = cnt0;
 65 
 66     while (cnt < n)
 67     {
 68         pop_heap(minHeap.begin(), minHeap.end());
 69         tmp = minHeap.back();
 70         minHeap.pop_back();
 71         int u = Find(tmp.v1);
 72         int v = Find(tmp.v2);
 73         if (u != v)
 74         {
 75             Join(tmp.v1, tmp.v2);
 76             ans += tmp.len;
 77             cnt++;
 78         }
 79     }
 80     return ans;
 81 }
 82 int main()
 83 {
 84     while (~scanf("%d", &n))
 85     {
 86         minHeap.clear();
 87         for (int i = 1; i <= n; i++)
 88         {
 89             for (int j = 1; j <= n; j++)
 90             {
 91                 int dis;
 92                 scanf("%d", &dis);
 93                 if (i == j)continue;
 94                 minHeap.push_back(side(i, j, dis));
 95             }
 96         }
 97         for (int i = 0; i <= n; i++) pre[i] = i;
 98         int cnt = 1;
 99         make_heap(minHeap.begin(), minHeap.end());
100         int ans=Kruskal(cnt);
101         printf("%d\n", ans);
102     }
103     return 0;
104 }
View Code

10、POJ 3026 Borg Maze

  题意:在一个y行 x列的迷宫中,有可行走的通路空格’ ‘,不可行走的墙’#’,还有两种英文字母A和S,现在从S出发,要求用最短的路径L连接所有字母,输出这条路径L的总长度。

  思路:先BFS求出所有两两之间的路径长度,然后建图求最小生成树。

  1 #include<iostream>
  2 #include<vector>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<set>
  6 #include<queue>
  7 #include<memory.h>
  8 using namespace std;
  9 int n,totalnum,rows,cols;
 10 const int maxn = 210;
 11 const int maxnum = 210;
 12 int pre[maxnum];
 13 char mp[maxn][maxn];
 14 bool vis[maxn][maxn];
 15 int id[maxn][maxn];
 16 struct side
 17 {
 18     int v1;
 19     int v2;
 20     int len;
 21     side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll)
 22     {
 23     }
 24     friend bool operator <(const side&a, const side&b)
 25     {
 26         return a.len > b.len;
 27     }
 28 };
 29 vector<side>minHeap;
 30 struct node
 31 {
 32     int x;
 33     int y;
 34     int stps;
 35     node(int xx = 0, int yy = 0,int stp=0) :x(xx), y(yy),stps(stp)
 36     {
 37     }
 38 };
 39 int dx[] = { 0,0,1,-1 };
 40 int dy[] = { 1,-1,0,0 };
 41 int Find(int x)
 42 {
 43     int r = x;
 44     while (r != pre[r])
 45     {
 46         r = pre[r];
 47     }
 48     int c = x, p;
 49     while (c != r)
 50     {
 51         p = pre[c];
 52         pre[c] = r;
 53         c = p;
 54     }
 55     return r;
 56 }
 57 bool Join(int x, int y)
 58 {
 59     int f1 = Find(x), f2 = Find(y);
 60     if (f1 != f2)
 61     {
 62         pre[f1] = f2;
 63         return false;
 64     }
 65     else return true;
 66 }
 67 void BFS(int rx,int ry)
 68 {
 69     memset(vis, 0, sizeof(vis));
 70     queue<node>q;
 71     q.push(node(rx, ry, 0));
 72     vis[rx][ry] = true;
 73     while (!q.empty())
 74     {
 75         node u = q.front();
 76         q.pop();
 77         for (int i = 0; i < 4; i++)
 78         {
 79             int tx = u.x + dx[i];
 80             int ty = u.y + dy[i];
 81             if (tx >= 0 && tx < rows&&ty >= 0 && ty < cols)
 82             {
 83                 if (mp[tx][ty] != '#' && !vis[tx][ty])
 84                 {
 85                     q.push(node(tx, ty, u.stps + 1));
 86                     vis[tx][ty] = true;
 87                     if (mp[tx][ty] == 'A' || mp[tx][ty] == 'S')
 88                     {
 89                         minHeap.push_back(side(id[rx][ry], id[tx][ty], u.stps + 1));
 90                     }
 91                 }
 92             }
 93         }
 94     }
 95 }
 96 int Kruskal(int cnt0)
 97 {
 98     side tmp;
 99     int ans = 0;
100     int cnt = cnt0;
101 
102     while (cnt < totalnum)
103     {
104         pop_heap(minHeap.begin(), minHeap.end());
105         tmp = minHeap.back();
106         minHeap.pop_back();
107         int u = Find(tmp.v1);
108         int v = Find(tmp.v2);
109         if (u != v)
110         {
111             Join(tmp.v1, tmp.v2);
112             ans += tmp.len;
113             cnt++;
114         }
115     }
116     return ans;
117 }
118 int main()
119 {
120     int t;
121     scanf("%d", &t);
122     while (t--)
123     {
124         scanf("%d%d", &cols, &rows);
125         minHeap.clear();
126         memset(id, 0, sizeof(id));
127         int idorder = 1;
128         while ((cin.get()) == ' ');
129         for (int i = 0; i<rows; i++)
130         {
131             for (int j = 0; j <cols; j++)
132             {
133                 scanf("%c",&mp[i][j]);
134                 if (mp[i][j] == 'S' || mp[i][j] == 'A')
135                 {
136                     id[i][j] = idorder++;
137                 }
138             }
139             cin.get();
140         }
141         for (int i = 0; i < rows; i++)
142         {
143             for (int j = 0; j < cols; j++)
144             {
145                 if (id[i][j])
146                 {
147                     BFS(i, j);
148                 }
149             }
150         }
151         for (int i = 0; i <= idorder; i++) pre[i] = i;
152         int cnt = 1;
153         totalnum = idorder - 1;
154         make_heap(minHeap.begin(), minHeap.end());
155         int ans = Kruskal(cnt);
156         printf("%d\n", ans);
157     }
158     return 0;
159 }
View Code

11、POJ 1679 The Unique MST

  题意:给出n个点,m条边,判断其最小生成树是否唯一。

  思路:先求出最小生成树,然后枚举删去最小生成树中的边,再求最小生成树,比较权值。

  1 #include<iostream>
  2 #include<vector>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<set>
  6 #include<queue>
  7 #include<memory.h>
  8 using namespace std;
  9 int n,m;
 10 const int maxn = 200;
 11 int pre[maxn];
 12 struct side
 13 {
 14     int v1;
 15     int v2;
 16     int len;
 17     side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll)
 18     {
 19     }
 20     friend bool operator <(const side&a, const side&b)
 21     {
 22         return a.len > b.len;
 23     }
 24 };
 25 vector<side>minHeap;
 26 vector<side>minheaptmp;
 27 struct node
 28 {
 29     int from;
 30     int to;
 31     int len;
 32     node(int ff=0,int tt = 0, int ll = 0) :from(ff),to(tt),len(ll)
 33     {
 34     }
 35 };
 36 vector<node>mintree;
 37 int Find(int x)
 38 {
 39     int r = x;
 40     while (r != pre[r])
 41     {
 42         r = pre[r];
 43     }
 44     int c = x, p;
 45     while (c != r)
 46     {
 47         p = pre[c];
 48         pre[c] = r;
 49         c = p;
 50     }
 51     return r;
 52 }
 53 bool Join(int x, int y)
 54 {
 55     int f1 = Find(x), f2 = Find(y);
 56     if (f1 != f2)
 57     {
 58         pre[f1] = f2;
 59         return false;
 60     }
 61     else return true;
 62 }
 63 
 64 int Kruskal(int du,int dv,int flag)
 65 {
 66     side tmp;
 67     int ans = 0;
 68     int cnt = 1;
 69     for (int i = 0; i <= n; i++)pre[i] = i;
 70     while (cnt < n&&!minHeap.empty())
 71     {
 72         pop_heap(minHeap.begin(), minHeap.end());
 73         tmp = minHeap.back();
 74         minHeap.pop_back();
 75         if ((tmp.v1 == du&&tmp.v2 == dv) || (tmp.v2 == du&&tmp.v1 == dv))continue;
 76         int u = Find(tmp.v1);
 77         int v = Find(tmp.v2);
 78         if (u != v)
 79         {
 80             Join(tmp.v1, tmp.v2);
 81             ans += tmp.len;
 82             if (flag)
 83             {
 84                 mintree.push_back(node(tmp.v1,tmp.v2, tmp.len));
 85             }
 86             cnt++;
 87         }
 88     }
 89     if (cnt < n&&minHeap.empty()) return -1;
 90     else return ans;
 91 }
 92 int main()
 93 {
 94     int t;
 95     scanf("%d", &t);
 96     while (t--)
 97     {
 98         scanf("%d%d", &n,&m);
 99         minHeap.clear();
100         mintree.clear();
101         for (int i = 0; i < m; i++)
102         {
103             int u, v, l;
104             scanf("%d%d%d", &u, &v, &l);
105             minHeap.push_back(side(u, v, l));
106         }
107         minheaptmp = minHeap;
108         int inians = Kruskal(0, 0, 1);
109         bool flag = true;
110         for (int i = 0; i < n - 1; i++)
111         {
112             int u = mintree[i].from;
113             int v = mintree[i].to;
114             minHeap = minheaptmp;
115             int ans = Kruskal(u, v, 0);
116             if (ans == inians)
117             {
118                 flag = false;
119                 break;
120             }
121         }
122         if (flag)printf("%d\n", inians);
123         else printf("Not Unique!\n");
124     }
125     return 0;
126 }
View Code

12、HDU 1233 还是畅通工程

  题意:某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。 

  思路:最小生成树。

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 int n;
 6 const int maxn = 110;
 7 int pre[maxn];
 8 struct side
 9 {
10     int v1;
11     int v2;
12     int len;
13     side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll)
14     {
15     }
16     friend bool operator <(const side&a, const side&b)
17     {
18         return a.len > b.len;
19     }
20 };
21 vector<side>minHeap;
22 int Find(int x)
23 {
24     int r = x;
25     while (r != pre[r])
26     {
27         r = pre[r];
28     }
29     int c = x, p;
30     while (c != r)
31     {
32         p = pre[c];
33         pre[c] = r;
34         c = p;
35     }
36     return r;
37 }
38 void Join(int x, int y)
39 {
40     int f1 = Find(x), f2 = Find(y);
41     if (f1 != f2) pre[f1] = f2;
42 }
43 int Kruskal()
44 {
45     side tmp;
46     int cnt = 1;
47     for (int i = 0; i <= n; i++)pre[i] = i;
48     int ans = 0;
49     while (cnt < n)
50     {
51         pop_heap(minHeap.begin(), minHeap.end());
52         tmp = minHeap.back();
53         minHeap.pop_back();
54         int u = Find(tmp.v1);
55         int v = Find(tmp.v2);
56         if (u != v)
57         {
58             Join(tmp.v1, tmp.v2);
59             ans += tmp.len;
60             cnt++;
61         }
62     }
63     return ans;
64 }
65 int main()
66 {
67     while (~scanf("%d", &n))
68     {
69         if (n == 0)break;
70         minHeap.clear();
71         for (int i = 0; i < n*(n - 1)/2; i++)
72         {
73             int u, v, l;
74             scanf("%d%d%d", &u, &v, &l);
75             minHeap.push_back(side(u, v, l));
76         }
77         make_heap(minHeap.begin(), minHeap.end());
78         int ans = Kruskal();
79         printf("%d\n", ans);
80     }
81     return 0;
82 }
View Code

13、hdu 1875 畅通工程再续

  题意:相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现。现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政府决定实现百岛湖的全畅通!经过考察小组RPRush对百岛湖的情况充分了解后,决定在符合条件的小岛间建上桥,所谓符合条件,就是2个小岛之间的距离不能小于10米,也不能大于1000米。当然,为了节省资金,只要求实现任意2个小岛之间有路通即可。其中桥的价格为 100元/米。

  思路:最小生成树模板。

 1 #include<iostream>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 int n;
 6 const int maxn = 110;
 7 int pre[maxn];
 8 struct side
 9 {
10     int v1;
11     int v2;
12     double len;
13     side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll)
14     {
15     }
16     friend bool operator <(const side&a, const side&b)
17     {
18         return a.len > b.len;
19     }
20 };
21 vector<side>minHeap;
22 
23 struct point
24 {
25     int x;
26     int y;
27 }points[maxn];
28 int Find(int x)
29 {
30     int r = x;
31     while (r != pre[r])
32     {
33         r = pre[r];
34     }
35     int c = x, p;
36     while (c != r)
37     {
38         p = pre[c];
39         pre[c] = r;
40         c = p;
41     }
42     return r;
43 }
44 void Join(int x, int y)
45 {
46     int f1 = Find(x), f2 = Find(y);
47     if (f1 != f2) pre[f1] = f2;
48 }
49 double Kruskal()
50 {
51     side tmp;
52     int cnt = 1;
53     for (int i = 0; i <= n; i++)pre[i] = i;
54     double ans = 0;
55     while (cnt < n&&!minHeap.empty())
56     {
57         pop_heap(minHeap.begin(), minHeap.end());
58         tmp = minHeap.back();
59         minHeap.pop_back();
60         int u = Find(tmp.v1);
61         int v = Find(tmp.v2);
62         if (u != v)
63         {
64             Join(tmp.v1, tmp.v2);
65             ans += tmp.len;
66             cnt++;
67         }
68     }
69     if (minHeap.empty() && cnt < n)return -1;
70     else return ans;
71 }
72 int main()
73 {
74     int t;
75     scanf("%d", &t);
76     while (t--)
77     {
78         scanf("%d", &n);
79         minHeap.clear();
80         for (int i = 1; i <= n; i++)
81         {
82             scanf("%d%d",&points[i].x, &points[i].y);
83         }
84         for (int i = 1; i <= n; i++)
85         {
86             for (int j = i + 1; j <= n; j++)
87             {
88                 double dis = sqrt((points[i].x - points[j].x)*(points[i].x - points[j].x) + (points[i].y - points[j].y)*(points[i].y - points[j].y));
89                 if (dis < 10 || dis>1000)continue;
90                 minHeap.push_back(side(i, j, dis));
91             }
92         }
93         make_heap(minHeap.begin(), minHeap.end());
94         double ans = Kruskal();
95         if(ans>0)printf("%.1lf\n", ans*100);
96         else printf("oh!\n");
97     }
98     return 0;
99 }
View Code

 14、HDU 4081 Qin Shi Huang's National Road System(次小生成树模板)

  题意:给定n个点的点权及相互间的边权,求生成树,现在能够使得其中一条边的边权变为0,求该0值边所连的两点的点权和/剩下的树边权的和的最大值。

  思路:找到最小生成树,然后添加一条边构成环,再删掉环中属于最小树的最大边,用这种方法遍历所有边以找到最终ans。

  1 //次小生成树可由最小生成树换一条边得到.
  2 //题意:
  3 //有n个城市,秦始皇要修用n-1条路把它们连起来,要求从任一点出发,都可以到达其它的任意点。秦始皇希望这所有n-1条路长度之和最短。然后徐福突然有冒出来,说是他有魔法,可以不用人力、财力就变出其中任意一条路出来。
  4 //秦始皇希望徐福能把要修的n - 1条路中最长的那条变出来,但是徐福希望能把要求的人力数量最多的那条变出来。对于每条路所需要的人力,是指这条路连接的两个城市的人数之和。
  5 //最终,秦始皇给出了一个公式,A / B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n - 2条路径长度之和,选使得A / B值最大的那条。
  6 
  7 #include<iostream>
  8 #include<algorithm>
  9 using namespace std;
 10 int n;//结点(城市)数目
 11 const int maxn = 1010;
 12 struct node
 13 {
 14     int x;
 15     int y;
 16     int v;
 17 }points[maxn];//记录每个城市的坐标,以及城市的人口(结点值)
 18 double dis[maxn][maxn];//根据点的坐标得到两点之间的距离
 19 bool mintree[maxn][maxn];//标记最小生成树的边
 20 double pathmax[maxn][maxn];//记录最小生成树中两点间路径的最大权值
 21 int pre[maxn];//标记生成最小生成树中,每个结点的父结点
 22 bool vis[maxn];//记录加入最小生成树的结点
 23 
 24 void Init()//初始化:得到结点间的距离
 25 {
 26     for (int i = 1; i <= n; i++)
 27     {
 28         dis[i][i] = 0;
 29         for (int j = i + 1; j <= n; j++)
 30         {
 31             double distance = sqrt((points[i].x - points[j].x)*(points[i].x - points[j].x) + (points[i].y - points[j].y)*(points[i].y - points[j].y));
 32             dis[i][j] = dis[j][i] = distance;
 33         }
 34     }
 35 }
 36 
 37 double Prime()
 38 {
 39     memset(vis, 0, sizeof(vis));
 40     memset(mintree, 0, sizeof(mintree));
 41     memset(pathmax, 0, sizeof(pathmax));
 42     double sum = 0;
 43     double mincost[maxn];//记录当前权值
 44     for (int i = 1; i <= n; i++)
 45     {
 46         mincost[i] = dis[1][i];
 47         pre[i] = 1;
 48     }
 49     vis[1] = true;
 50     for (int i = 1; i < n; i++)
 51     {//每次选1个点加入
 52         int u = 0;
 53         for (int j = 1; j <= n; j++)
 54         {//从未加入的点找到一条最小的边
 55             if (!vis[j])
 56             {
 57                 if (u == 0 || mincost[j] < mincost[u])
 58                 {
 59                     u = j;
 60                 }
 61             }
 62         }
 63         mintree[u][pre[u]] = mintree[pre[u]][u] = true;
 64         sum += dis[u][pre[u]];
 65         vis[u] = true;
 66 
 67         for (int j = 1; j <= n; j++)
 68         {
 69             if (vis[j] && j != u)
 70             {//更新树中两点路径的最大权值
 71                 pathmax[u][j] = pathmax[j][u] = max(pathmax[j][pre[u]], dis[u][pre[u]]);
 72             }
 73             else if (!vis[j])
 74             {//更新已选结点到未选结点的最短距离
 75                 if (dis[u][j] < mincost[j])
 76                 {
 77                     mincost[j] = dis[u][j];
 78                     pre[j] = u;
 79                 }
 80             }
 81         }
 82     }
 83     return sum;
 84 }
 85 int main()
 86 {
 87     int t;
 88     scanf("%d", &t);
 89     while (t--)//t组测试数据
 90     {
 91         scanf("%d", &n);
 92         for (int i = 1; i <= n; i++)
 93         {
 94             scanf("%d%d%d", &points[i].x, &points[i].y, &points[i].v);
 95         }
 96         Init();
 97         double inilen=Prime();
 98         double ratio = 0;
 99         for (int i = 1; i <= n; i++)
100         {
101             for (int j = 1; j <= n; j++)
102             {
103                 if (i == j) continue;
104                 if (mintree[i][j])//如果选择的是最小生成树中的一条边,则剩下的路径长为inilen - dis[i][j]
105                 {
106                     ratio = max(ratio, (points[i].v + points[j].v) / (inilen - dis[i][j]));
107                 }
108                 else//如果选择的不是最小生成树中的边,那么加入该条边后就会形成一个环,需要删去最小生成树中i到j的路径中权值最大的边
109                 {
110                     ratio = max(ratio, (points[i].v + points[j].v) / (inilen - pathmax[i][j]));
111                 }
112             }
113         }
114         printf("%.2lf\n", ratio);
115     }
116     return 0;
117 }
View Code

15、UVA 10600 ACM Contest and Blackout

  题意:给出一张图,问这张图的最小生成树的权值和次小生成树的权值,如果有两个最小生成树,则输出的权值都为最小生成树的权值。

  思路:模板题。

  1 #include<iostream>
  2 #include<vector>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<set>
  6 #include<queue>
  7 #include<memory.h>
  8 using namespace std;
  9 int n, m;
 10 const int maxn = 110;
 11 const int maxm = 10010;
 12 const int INF = 0x7fffffff;
 13 int pre[maxn];
 14 struct side
 15 {
 16     int v1;
 17     int v2;
 18     int len;
 19     side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll)
 20     {
 21     }
 22     friend bool operator <(const side&a, const side&b)
 23     {
 24         return a.len < b.len;
 25     }
 26 };
 27 vector<side>minHeap;
 28 struct node
 29 {
 30     int to;
 31     int len;
 32     node(int tt = 0, int ll = 0) : to(tt), len(ll)
 33     {
 34     }
 35 };
 36 vector<node>mintree[maxn];
 37 vector<int>mintreenodes;
 38 int pathmax[maxn][maxn];
 39 void DFS(int cur, int fa,int dis)
 40 {
 41     int sz = mintreenodes.size();
 42     for (int i = 0; i < sz; i++)
 43     {
 44         int prefa = mintreenodes[i];
 45         pathmax[prefa][cur] = pathmax[cur][prefa] = max(pathmax[prefa][fa], dis);
 46     }
 47     mintreenodes.push_back(cur);
 48     for (int i = 0; i < mintree[cur].size(); i++)
 49     {
 50         if (mintree[cur][i].to!=fa)
 51         {
 52             DFS(mintree[cur][i].to, cur, mintree[cur][i].len);
 53         }
 54     }
 55 }
 56 int Find(int x)
 57 {
 58     int r = x;
 59     while (r != pre[r])
 60     {
 61         r = pre[r];
 62     }
 63     int c = x, p;
 64     while (c != r)
 65     {
 66         p = pre[c];
 67         pre[c] = r;
 68         c = p;
 69     }
 70     return r;
 71 }
 72 bool Join(int x, int y)
 73 {
 74     int f1 = Find(x), f2 = Find(y);
 75     if (f1 != f2)
 76     {
 77         pre[f1] = f2;
 78         return false;
 79     }
 80     else return true;
 81 }
 82 
 83 bool vis[maxm];
 84 int Kruskal()
 85 {
 86     side tmp;
 87     int ans = 0;
 88     for (int i = 0; i <= n; i++)pre[i] = i;
 89     sort(minHeap.begin(), minHeap.end());
 90     for(int i=0;i<m;i++)
 91     {
 92         tmp = minHeap[i];
 93         int u = Find(tmp.v1);
 94         int v = Find(tmp.v2);
 95         if (u != v)
 96         {
 97             Join(tmp.v1, tmp.v2);
 98             ans += tmp.len;
 99             mintree[tmp.v1].push_back(node(tmp.v2, tmp.len));
100             mintree[tmp.v2].push_back(node(tmp.v1, tmp.len));
101             vis[i] = true;
102         }
103     }
104     return ans;
105 }
106 int main()
107 {
108     int t;
109     scanf("%d", &t);
110     while (t--)
111     {
112         scanf("%d%d", &n, &m);
113 
114         minHeap.clear();
115         mintreenodes.clear();
116         memset(pathmax, 0, sizeof(pathmax));
117         for (int i = 0; i <= n;i++)mintree[i].clear();
118 
119 
120         for (int i = 0; i < m; i++)
121         {
122             int u, v, l;
123             scanf("%d%d%d", &u, &v, &l);
124             minHeap.push_back(side(u, v, l));
125         }
126         memset(vis, 0, sizeof(vis));
127         int inians = Kruskal();
128         DFS(1, 0, 0);
129         int len2 = INF;
130         for (int i = 0; i < m; i++)
131         {
132             if (!vis[i])
133             {
134                 len2 = min(len2, inians + minHeap[i].len - pathmax[minHeap[i].v1][minHeap[i].v2]);
135             }
136         }
137         printf("%d %d\n", inians, len2);
138     }
139     return 0;
140 }
View Code

 16、UVA 10462 Is There A Second Way Left?

  题意:给出无向图,问能否有一最小生成树,若有,问能否有一不同的次小生成树。

  思路:次小生成树模板。

  1 #include<iostream>
  2 #include<vector>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<set>
  6 #include<queue>
  7 #include<memory.h>
  8 using namespace std;
  9 int n, m;
 10 const int maxn = 110;
 11 const int maxm = 10010;
 12 const int INF = 0x7fffffff;
 13 int pre[maxn];
 14 struct side
 15 {
 16     int v1;
 17     int v2;
 18     int len;
 19     side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll)
 20     {
 21     }
 22     friend bool operator <(const side&a, const side&b)
 23     {
 24         return a.len < b.len;
 25     }
 26 };
 27 vector<side>minHeap;
 28 struct node
 29 {
 30     int to;
 31     int len;
 32     node(int tt = 0, int ll = 0) : to(tt), len(ll)
 33     {
 34     }
 35 };
 36 vector<node>mintree[maxn];
 37 vector<int>mintreenodes;
 38 int pathmax[maxn][maxn];
 39 void DFS(int cur, int fa, int dis)
 40 {
 41     int sz = mintreenodes.size();
 42     for (int i = 0; i < sz; i++)
 43     {
 44         int prefa = mintreenodes[i];
 45         pathmax[prefa][cur] = pathmax[cur][prefa] = max(pathmax[prefa][fa], dis);
 46     }
 47     mintreenodes.push_back(cur);
 48     for (int i = 0; i < mintree[cur].size(); i++)
 49     {
 50         if (mintree[cur][i].to != fa)
 51         {
 52             DFS(mintree[cur][i].to, cur, mintree[cur][i].len);
 53         }
 54     }
 55 }
 56 int Find(int x)
 57 {
 58     int r = x;
 59     while (r != pre[r])
 60     {
 61         r = pre[r];
 62     }
 63     int c = x, p;
 64     while (c != r)
 65     {
 66         p = pre[c];
 67         pre[c] = r;
 68         c = p;
 69     }
 70     return r;
 71 }
 72 bool Join(int x, int y)
 73 {
 74     int f1 = Find(x), f2 = Find(y);
 75     if (f1 != f2)
 76     {
 77         pre[f1] = f2;
 78         return false;
 79     }
 80     else return true;
 81 }
 82 
 83 bool vis[maxm];
 84 int Kruskal()
 85 {
 86     side tmp;
 87     int ans = 0;
 88     for (int i = 0; i <= n; i++)pre[i] = i;
 89     sort(minHeap.begin(), minHeap.end());
 90     int cnt = 1;
 91     for (int i = 0; i<m; i++)
 92     {
 93         tmp = minHeap[i];
 94         int u = Find(tmp.v1);
 95         int v = Find(tmp.v2);
 96         if (u != v)
 97         {
 98             Join(tmp.v1, tmp.v2);
 99             ans += tmp.len;
100             mintree[tmp.v1].push_back(node(tmp.v2, tmp.len));
101             mintree[tmp.v2].push_back(node(tmp.v1, tmp.len));
102             vis[i] = true;
103             cnt++;
104         }
105     }
106     if (cnt < n)return -1;
107     else return ans;
108 }
109 int main()
110 {
111     int t;
112     scanf("%d", &t);
113     int Case = 1;
114     while (t--)
115     {
116         scanf("%d%d", &n, &m);
117 
118         minHeap.clear();
119         mintreenodes.clear();
120         memset(pathmax, 0, sizeof(pathmax));
121         for (int i = 0; i <= n; i++)mintree[i].clear();
122 
123 
124         for (int i = 0; i < m; i++)
125         {
126             int u, v, l;
127             scanf("%d%d%d", &u, &v, &l);
128             minHeap.push_back(side(u, v, l));
129         }
130         memset(vis, 0, sizeof(vis));
131         int inians = Kruskal();
132         if (inians == -1)
133         {
134             printf("Case #%d : No way\n", Case++);
135             continue;
136         }
137         DFS(1, 0, 0);
138         int len2 = INF;
139         for (int i = 0; i < m; i++)
140         {
141             if (!vis[i])
142             {
143                 len2 = min(len2, inians + minHeap[i].len - pathmax[minHeap[i].v1][minHeap[i].v2]);
144             }
145         }
146         if (len2!=INF)
147         {
148             printf("Case #%d : %d\n", Case++, len2);
149         }
150         else printf("Case #%d : No second way\n", Case++);
151     }
152     return 0;
153 }
View Code

 17、poj 3164 Command Network

  题意:给出n个点的坐标,给出m条有向边,问以1为根的最小树形图的权值。

  思路:最小树形图模板。用邻接矩阵表示,适合点小于1000的情况

  1 #include <iostream>
  2 #include <cmath>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <cstdlib>
  6 #include <algorithm>
  7 #include <string>
  8 typedef long long LL;
  9 using namespace std;
 10 const int maxv = 110;
 11 const int maxe = 10100;
 12 const int INF = 0x3f3f3f3f;
 13 int n, m;
 14 double mp[maxv][maxv];
 15 //求具有V个点,以root为根节点的图mp的最小树形图
 16 double zhuliu(int root, int V)
 17 {//mp[][]存[i][j]有向边的权,目前没有把缩点展开
 18     //如果不存在最小树形图,返回-1,否则返回最小树形图的权值
 19     bool visited[maxv];
 20     bool flag[maxv];//缩点标记为true,否则仍然存在
 21     int pre[maxv];//点i的父节点为pre[i]
 22     double sum = 0;//最小树形图的权值
 23     int i, j, k;
 24     for (i = 0; i <= V; i++) flag[i] = false, mp[i][i] = 1.0*INF;
 25     pre[root] = root;
 26     while (true)
 27     {
 28         for (i = 1; i <= V; i++)
 29         {//求最短弧集合E0
 30             if (flag[i] || i == root) continue;//跳过根和缩点
 31             pre[i] = i;
 32             for (j = 1; j <= V; j++)//找最小的入边
 33                 if (!flag[j] && mp[j][i] < mp[pre[i]][i])
 34                     pre[i] = j;
 35             if (pre[i] == i) return -1;//除了跟以外有点没有入边,则根无法到达它
 36         }
 37         for (i = 1; i <= V; i++)
 38         {//检查E0
 39             if (flag[i] || i == root) continue;
 40             for (j = 1; j <= V; j++) visited[j] = false;
 41             visited[root] = true;
 42             j = i;//从当前点开始找环
 43             do
 44             {
 45                 visited[j] = true;
 46                 j = pre[j];
 47             } while (!visited[j]);
 48             if (j == root)continue;//没找到环
 49             i = j;//收缩G中的有向环
 50             do
 51             {//将整个环的取值保存,累计计入原图的最小树形图
 52                 sum += mp[pre[j]][j];
 53                 j = pre[j];
 54             } while (j != i);
 55             j = i;
 56             do
 57             {//对于环上的点有关的边,修改其权值
 58                 for (k = 1; k <= V; k++)
 59                     if (!flag[k] && mp[k][j] < INF && k != pre[j])
 60                         mp[k][j] -= mp[pre[j]][j];
 61                 j = pre[j];
 62             } while (j != i);
 63             for (j = 1; j <= V; j++)
 64             {//缩点,将整个环缩成i号点,所有与环上的点有关的边转移到点i
 65                 if (j == i) continue;
 66                 for (k = pre[i]; k != i; k = pre[k])
 67                 {
 68                     if (mp[k][j] < mp[i][j]) mp[i][j] = mp[k][j];
 69                     if (mp[j][k] < mp[j][i]) mp[j][i] = mp[j][k];
 70                 }
 71             }
 72             for (j = pre[i]; j != i; j = pre[j]) flag[j] = true;//标记环上其他点为被缩掉
 73             break;//当前环缩点结束,形成新的图G',跳出继续求G'的最小树形图
 74         }
 75         if (i > V)
 76         {//如果所有的点都被检查且没有环存在,现在的最短弧集合E0就是最小树形图.累计计入sum,算法结束
 77             for (i = 1; i <= V; i++)
 78                 if (!flag[i] && i != root) sum += mp[pre[i]][i];
 79             break;
 80         }
 81     }
 82     return sum;
 83 }
 84 struct node
 85 {
 86     int x;
 87     int y;
 88 }nodes[maxv];
 89 int main()
 90 {
 91     while (~scanf("%d%d", &n, &m))
 92     {
 93         for (int i = 1; i <= n; i++) scanf("%d%d", &nodes[i].x, &nodes[i].y);
 94         for (int i = 1; i <= n; i++)
 95         {
 96             for (int j = 1; j <= n; j++) mp[i][j] = 1.0*INF;
 97         }
 98         for (int i = 1; i <= m; i++)
 99         {
100             int u, v;
101             scanf("%d%d", &u, &v);
102             double dis = sqrt(1.0*(nodes[u].x - nodes[v].x)*(nodes[u].x - nodes[v].x) + (nodes[u].y - nodes[v].y)*(nodes[u].y - nodes[v].y));
103             mp[u][v] = dis;
104         }
105         double ans = zhuliu(1, n);
106         if (ans == -1)printf("poor snoopy\n");
107         else printf("%.2lf\n", ans);
108     }
109     return 0;
110 }
View Code

 18、UVA 11183 Teen Girl Squad

  题意:给出n个女孩,给出A打给B的电话费用,现在1号女孩要把信息传给其他所有人,问最少花费。

  思路:最小树形图。注意输入时编号从0开始。

  1 #include <iostream>
  2 #include <cmath>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <cstdlib>
  6 #include <algorithm>
  7 #include <string>
  8 typedef long long LL;
  9 using namespace std;
 10 const int maxv = 1010;
 11 const int maxe = 40100;
 12 const int INF = 0x3f3f3f3f;
 13 int n, m;
 14 int mp[maxv][maxv];
 15 //求具有V个点,以root为根节点的图mp的最小树形图
 16 int zhuliu(int root, int V)
 17 {//mp[][]存[i][j]有向边的权,目前没有把缩点展开
 18  //如果不存在最小树形图,返回-1,否则返回最小树形图的权值
 19     //结点编号从1开始
 20     bool visited[maxv];
 21     bool flag[maxv];//缩点标记为true,否则仍然存在
 22     int pre[maxv];//点i的父节点为pre[i]
 23     int sum = 0;//最小树形图的权值
 24     int i, j, k;
 25     for (i = 0; i <= V; i++) flag[i] = false, mp[i][i] = INF;
 26     pre[root] = root;
 27     while (true)
 28     {
 29         for (i = 1; i <= V; i++)
 30         {//求最短弧集合E0
 31             if (flag[i] || i == root) continue;//跳过根和缩点
 32             pre[i] = i;
 33             for (j = 1; j <= V; j++)//找最小的入边
 34                 if (!flag[j] && mp[j][i] < mp[pre[i]][i])
 35                     pre[i] = j;
 36             if (pre[i] == i) return -1;//除了跟以外有点没有入边,则根无法到达它
 37         }
 38         for (i = 1; i <= V; i++)
 39         {//检查E0
 40             if (flag[i] || i == root) continue;
 41             for (j = 1; j <= V; j++) visited[j] = false;
 42             visited[root] = true;
 43             j = i;//从当前点开始找环
 44             do
 45             {
 46                 visited[j] = true;
 47                 j = pre[j];
 48             } while (!visited[j]);
 49             if (j == root)continue;//没找到环
 50             i = j;//收缩G中的有向环
 51             do
 52             {//将整个环的取值保存,累计计入原图的最小树形图
 53                 sum += mp[pre[j]][j];
 54                 j = pre[j];
 55             } while (j != i);
 56             j = i;
 57             do
 58             {//对于环上的点有关的边,修改其权值
 59                 for (k = 1; k <= V; k++)
 60                     if (!flag[k] && mp[k][j] < INF && k != pre[j])
 61                         mp[k][j] -= mp[pre[j]][j];
 62                 j = pre[j];
 63             } while (j != i);
 64             for (j = 1; j <= V; j++)
 65             {//缩点,将整个环缩成i号点,所有与环上的点有关的边转移到点i
 66                 if (j == i) continue;
 67                 for (k = pre[i]; k != i; k = pre[k])
 68                 {
 69                     if (mp[k][j] < mp[i][j]) mp[i][j] = mp[k][j];
 70                     if (mp[j][k] < mp[j][i]) mp[j][i] = mp[j][k];
 71                 }
 72             }
 73             for (j = pre[i]; j != i; j = pre[j]) flag[j] = true;//标记环上其他点为被缩掉
 74             break;//当前环缩点结束,形成新的图G',跳出继续求G'的最小树形图
 75         }
 76         if (i > V)
 77         {//如果所有的点都被检查且没有环存在,现在的最短弧集合E0就是最小树形图.累计计入sum,算法结束
 78             for (i = 1; i <= V; i++)
 79                 if (!flag[i] && i != root) sum += mp[pre[i]][i];
 80             break;
 81         }
 82     }
 83     return sum;
 84 }
 85 struct node
 86 {
 87     int x;
 88     int y;
 89 }nodes[maxv];
 90 int main()
 91 {
 92     int t;
 93     scanf("%d", &t);
 94     int Case = 1;
 95     while (t--)
 96     {
 97         scanf("%d%d", &n, &m);
 98         for (int i = 1; i <= n; i++)
 99         {
100             for (int j = 1; j <= n; j++) mp[i][j] = INF;
101         }
102         for (int i = 1; i <= m; i++)
103         {
104             int u, v, w;
105             scanf("%d%d%d", &u, &v, &w);
106             u++, v++;
107             mp[u][v] = w;
108         }
109         int ans = zhuliu(1, n);
110         if (ans == -1)printf("Case #%d: Possums!\n", Case++);
111         else printf("Case #%d: %d\n", Case++, ans);
112     }
113     return 0;
114 }
View Code

 19、hdu 2121 Ice_cream’s world II

  题意:有n个城市和m条有向边,现在要让你选一处为城堡,使其到其他城市的路径长度最小。

  思路:无根最小树形图。建立超级源点,和其他点建立一条有向边,边权为原本所有有向边的权值之和+1.最后,最小树形图的根为和超级源点相连的点。由于城市数目过多,不能再用邻接矩阵,用边表存。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 using namespace std;
  5 #define N 1010
  6 #define INF 0x7f7f7f7f
  7 struct Edge
  8 {
  9     int u, v, w;
 10 } e[N*N];
 11 int cnt;
 12 int in[N];
 13 int vis[N], pre[N], id[N];
 14 int minroot;
 15 void addedge(int u, int v, int w)
 16 {
 17     e[cnt].u = u;
 18     e[cnt].v = v;
 19     e[cnt++].w = w;
 20 }
 21 int Directed_MST(int root, int NV, int NE)
 22 {//root根结点,nv为结点数目,ne为边数
 23     int ret = 0;//存储最小树形图的边权
 24     while (true)
 25     {
 26         //步骤1:找到最小边
 27         for (int i = 0; i < NV; i++)
 28             in[i] = INF;
 29         memset(pre, -1, sizeof(pre));
 30         for (int i = 0; i < NE; i++)
 31         {
 32             int u = e[i].u, v = e[i].v;
 33             if (e[i].w < in[v] && u != v&&v!=root)
 34             {
 35                 pre[v] = u;
 36                 in[v] = e[i].w;
 37                 if (u == root) minroot = i;//存的是边的编号
 38             }
 39         }
 40         for (int i = 0; i < NV; i++)
 41         {
 42             if (i == root) continue;
 43             if (in[i] == INF) return -1;//除了根节点以外有点没有入边,则根无法到达他
 44         }
 45         int cntnode = 0;
 46         memset(id, -1, sizeof(id));
 47         memset(vis, -1, sizeof(vis));
 48         //找环
 49         in[root] = 0;
 50         for (int i = 0; i < NV; i++) //标记每个环,编号
 51         {
 52             ret += in[i];
 53             int v = i;
 54             while (vis[v] != i && id[v] == -1 && v != root)
 55             {
 56                 vis[v] = i;
 57                 v = pre[v];
 58             }
 59             if (v != root && id[v] == -1)//成环
 60             {
 61                 for (int u = pre[v]; u != v; u = pre[u])
 62                 {//编号
 63                     id[u] = cntnode;
 64                 }
 65                 id[v] = cntnode++;
 66             }
 67         }
 68         if (cntnode == 0) break;//无环
 69         for (int i = 0; i < NV; i++)
 70             if (id[i] == -1)
 71                 id[i] = cntnode++;
 72         //步骤3:缩点,重新标记
 73         for (int i = 0; i < NE; i++)
 74         {
 75             int u = e[i].u;
 76             int v = e[i].v;
 77             e[i].u = id[u];
 78             e[i].v = id[v];
 79             if (e[i].u != e[i].v) e[i].w -= in[v];
 80         }
 81         NV = cntnode;
 82         root = id[root];
 83     }
 84     return ret;//最小树形图的权值
 85 }
 86 
 87 int main()
 88 {
 89     int n, m, sum;
 90     int u, v, w;
 91     while (~scanf("%d%d", &n, &m))
 92     {
 93         cnt = 0; sum = 0;
 94         for (int i = 0; i<m; i++)
 95         {
 96             scanf("%d %d %d", &u, &v, &w);
 97             addedge(u + 1, v + 1, w);
 98             sum += w;
 99         }
100         sum++;
101         for (int i = 1; i <= n; i++)
102             addedge(0, i, sum);
103         int ans = Directed_MST(0, n + 1, cnt);
104         if (ans == -1 || ans >= 2 * sum)
105             printf("impossible\n\n");
106         else
107             printf("%d %d\n\n", ans - sum, minroot - m);//第m+i条边和超级源点相连
108     }
109     return 0;
110 }
View Code

 20、hdu 4009 Transfer water

  题意:有N个点,每个点都有相应的三维坐标(x,y,z),现在要求每个点都能获得水,或者水的方式有两种

  1.自己挖井,费用为X * 海拔高度z

  2.铺设管道引水(只能从指定的点引水):a.如果海拔高度小于引水处,费用为两地曼哈顿距离*Y ;b.如果海拔高度大于饮水处,费用为两地曼哈顿距离*Y + Z

  求最小花费。

  思路:设置一个虚根,虚根引向所有的点,权值为挖井的费用,接着按照要求连边,求出最小树形图即可。

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 using namespace std;
  5 #define N 1010
  6 #define INF 0x7f7f7f7f
  7 struct Edge
  8 {
  9     int u, v, w;
 10 } e[N*N];
 11 int cnt;
 12 int in[N];
 13 int vis[N], pre[N], id[N];
 14 int minroot;
 15 void addedge(int u, int v, int w)
 16 {
 17     e[cnt].u = u;
 18     e[cnt].v = v;
 19     e[cnt++].w = w;
 20 }
 21 int Directed_MST(int root, int NV, int NE)
 22 {//root根结点,nv为结点数目,ne为边数
 23     int ret = 0;//存储最小树形图的边权
 24     while (true)
 25     {
 26         //步骤1:找到最小边
 27         for (int i = 0; i < NV; i++)
 28             in[i] = INF;
 29         memset(pre, -1, sizeof(pre));
 30         for (int i = 0; i < NE; i++)
 31         {
 32             int u = e[i].u, v = e[i].v;
 33             if (e[i].w < in[v] && u != v&&v != root)
 34             {
 35                 pre[v] = u;
 36                 in[v] = e[i].w;
 37                 if (u == root) minroot = i;//存的是边的编号
 38             }
 39         }
 40         for (int i = 0; i < NV; i++)
 41         {
 42             if (i == root) continue;
 43             if (in[i] == INF) return -1;//除了根节点以外有点没有入边,则根无法到达他
 44         }
 45         int cntnode = 0;
 46         memset(id, -1, sizeof(id));
 47         memset(vis, -1, sizeof(vis));
 48         //找环
 49         in[root] = 0;
 50         for (int i = 0; i < NV; i++) //标记每个环,编号
 51         {
 52             ret += in[i];
 53             int v = i;
 54             while (vis[v] != i && id[v] == -1 && v != root)
 55             {
 56                 vis[v] = i;
 57                 v = pre[v];
 58             }
 59             if (v != root && id[v] == -1)//成环
 60             {
 61                 for (int u = pre[v]; u != v; u = pre[u])
 62                 {//编号
 63                     id[u] = cntnode;
 64                 }
 65                 id[v] = cntnode++;
 66             }
 67         }
 68         if (cntnode == 0) break;//无环
 69         for (int i = 0; i < NV; i++)
 70             if (id[i] == -1)
 71                 id[i] = cntnode++;
 72         //步骤3:缩点,重新标记
 73         for (int i = 0; i < NE; i++)
 74         {
 75             int u = e[i].u;
 76             int v = e[i].v;
 77             e[i].u = id[u];
 78             e[i].v = id[v];
 79             if (e[i].u != e[i].v) e[i].w -= in[v];
 80         }
 81         NV = cntnode;
 82         root = id[root];
 83     }
 84     return ret;//最小树形图的权值
 85 }
 86 struct house
 87 {
 88     int x;
 89     int y;
 90     int h;
 91 }hh[N];
 92 int main()
 93 {
 94     int n, m, sum,X,Y,Z;
 95     int u, v, w;
 96     while (~scanf("%d%d%d%d", &n, &X,&Y,&Z))
 97     {
 98         if (n == 0 && X == 0 && Y == 0 && Z == 0)break;
 99         cnt = 0;
100         for (int i = 1; i<=n; i++)
101         {
102             scanf("%d%d%d", &hh[i].x, &hh[i].y, &hh[i].h);
103         }
104         for (int i = 1; i <= n; i++)
105         {
106             int num;
107             scanf("%d", &num);
108             for (int j = 0; j < num; j++)
109             {
110                 int v;
111                 scanf("%d", &v);
112                 if (v != i)
113                 {
114                     int cost;
115                     if (hh[v].h <= hh[i].h)cost = (abs(hh[v].x - hh[i].x) + abs(hh[v].y - hh[i].y)+abs(hh[v].h-hh[i].h))*Y;
116                     else cost = (abs(hh[v].x - hh[i].x) + abs(hh[v].y - hh[i].y)+abs(hh[v].h - hh[i].h))*Y + Z;
117                     addedge(i, v, cost);
118                 }
119             }
120         }
121         for (int i = 1; i <= n; i++)
122             addedge(0, i, hh[i].h*X);
123         int ans = Directed_MST(0, n + 1, cnt);
124         if (ans == -1)
125             printf("poor XiaoA\n");
126         else
127             printf("%d\n", ans);
128     }
129     return 0;
130 }
View Code

 21、UVA 10766 Organising the Organisation(无向图生成树计数--Matrix-Tree定理)

  题意:给出n, m, k。表示n个点,其中m条边不能直接连通,求生成树个数。

  思路:生成树计数:Matrix-Tree定理模板。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N = 55;
 8 typedef long long LL;
 9 LL C[N][N];
10 bool can[N][N];
11 //对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。
12 //所谓n - 1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。
13 LL det(LL c[][N], int n)//生成树计数:Matrix-Tree定理
14 {//c[N][N]为Kirchhoff矩阵,=它的度数矩阵D减去它的邻接矩阵A(A[i][j]=1表示i和j有边相连)
15     LL ret = 1;
16     for (int i = 1; i<n; i++)
17     {
18         for (int j = i + 1; j<n; j++)
19             while (c[j][i])
20             {
21                 LL t = c[i][i] / c[j][i];
22                 for (int k = i; k<n; k++)
23                     c[i][k] = (c[i][k] - c[j][k] * t);
24                 for (int k = i; k<n; k++)
25                     swap(c[i][k], c[j][k]);
26                 ret = -ret;
27             }
28         if (c[i][i] == 0)
29             return 0;
30         ret = ret*c[i][i];
31     }
32     if (ret<0)
33         ret = -ret;
34     return ret;
35 }
36 
37 int main()
38 {
39     int n, m, k;
40     while (~scanf("%d%d%d", &n, &m,&k))
41     {
42         memset(C, 0, sizeof(C));
43         memset(can, true, sizeof(can));
44         int u, v;
45         while (m--)
46         {
47             scanf("%d%d", &u, &v);
48             u--;
49             v--;
50             can[u][v] = can[v][u] = false;
51         }
52         for (int i = 0; i < n; i++)
53         {
54             for (int j = i+1; j < n; j++)
55             {
56                 if (can[i][j])
57                 {
58                     C[i][i]++, C[j][j]++;
59                     C[i][j] = -1, C[j][i] = -1;
60                 }
61             }
62         }
63         printf("%lld\n", det(C, n));
64     }
65     return 0;
66 }
View Code

 22、URAL 1627 Join

  题意:给出一张图,'.'表示卧室,‘*’表示茶水间。每个房间四周都有墙,现在要把相邻的卧室的墙打通,求有多少种方案使得两两之间只有一条通路。

  思路:给所有卧室编号,相邻的建边,求生成树个数。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N = 110;
 8 const int maxn = 15;
 9 typedef long long LL;
10 LL C[N][N];
11 int mp[maxn][maxn];
12 char s[maxn];
13 int dr[] = { 0,0,1,-1 };
14 int dc[] = { 1,-1,0,0 };
15 //对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。
16 //所谓n - 1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。
17 LL det(LL c[][N], int n)//生成树计数:Matrix-Tree定理
18 {//c[N][N]为Kirchhoff矩阵,=它的度数矩阵D减去它的邻接矩阵A(A[i][j]=1表示i和j有边相连)
19     LL ret = 1;
20     for (int i = 1; i<n; i++)
21     {
22         for (int j = i + 1; j<n; j++)
23             while (c[j][i])
24             {
25                 LL t = c[i][i] / c[j][i];
26                 for (int k = i; k<n; k++)
27                     c[i][k] = (c[i][k] - c[j][k] * t);
28                 for (int k = i; k<n; k++)
29                     swap(c[i][k], c[j][k]);
30                 ret = -ret;
31             }
32         if (c[i][i] == 0)
33             return 0;
34         ret = ret*c[i][i];
35     }
36     if (ret<0)
37         ret = -ret;
38     return ret;
39 }
40 
41 int main()
42 {
43     int n, m, k;
44     while (~scanf("%d%d", &n, &m))
45     {
46         memset(C, 0, sizeof(C));
47         memset(mp, -1, sizeof(mp));
48         int count = 0;
49         for(int i=0;i<n;i++)
50         {
51             scanf("%s", s);
52             for (int j = 0; j < m; j++)
53             {
54                 if (s[j] == '.') mp[i][j] = count++;
55             }
56         }
57         for (int i = 0; i < n; i++)
58         {
59             for (int j = 0; j < m; j++)
60             {
61                 if (mp[i][j]>-1)
62                 {
63                     for (int k = 0; k < 4; k++)
64                     {
65                         int di = i + dr[k];
66                         int dj = j + dc[k];
67                         if (di >= 0 && di < n&&dj >= 0 && dj<m&&mp[di][dj]>-1)
68                         {
69                             int u = mp[i][j], v = mp[di][dj];
70                             C[u][u]++;
71                             C[u][v]  = -1;
72                         }
73                     }
74                 }
75             }
76         }
77         printf("%lld\n", det(C, count));
78     }
79     return 0;
80 }
View Code

 22、HDU 4305 Lightning

  题意:闪电第一次会击中一个机器人然后会扩散到距离其不超过r并且之间无其他机器人的机器人的身上,然后继续扩散。问有多少种方案可以击中所有机器人。

  思路:按照要求建边,然后跑生成树计数模板。参考:http://www.cnblogs.com/flipped/p/5767113.html

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N = 310;
 8 const int MOD=10007;
 9 typedef long long LL;
10 LL C[N][N];
11 bool can[N][N];
12 
13 //对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。
14 //所谓n - 1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。
15 LL det(LL c[][N], int n)//生成树计数:Matrix-Tree定理
16 {//c[N][N]为Kirchhoff矩阵,=它的度数矩阵D减去它的邻接矩阵A(A[i][j]=1表示i和j有边相连)
17     LL ret = 1;
18     for (int i = 0; i < n; i++)
19     {
20         for (int j = 0; j < n; j++) c[i][j] %=MOD;
21     }
22     for (int i = 1; i<n; i++)
23     {
24         for (int j = i + 1; j<n; j++)
25             while (c[j][i])
26             {
27                 LL t = c[i][i] / c[j][i];//不用逆元,类似辗转相除
28                 for (int k = i; k<n; k++)
29                     c[i][k] = (c[i][k] - c[j][k] * t)%MOD;
30                 for (int k = i; k<n; k++)
31                     swap(c[i][k], c[j][k]);
32                 ret = -ret;
33             }
34         if (c[i][i] == 0)
35             return 0;
36         ret = ret*c[i][i]%MOD;
37     }
38     if (ret < 0)
39         return ret = (MOD + ret) % MOD;
40     return ret%MOD;
41 }
42 
43 struct point
44 {
45     int x;
46     int y;
47 }pp[N];
48 bool isok(const point&a, const point&b, const point&c,int r)
49 {//判断点a和点b是否距离小于要求值r,并且中间连线没有第三点c
50     if ((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y) > r*r)return false;
51     else
52     {
53         if ((c.y - a.y)*(b.x - a.x) == (b.y - a.y)*(c.x - a.x) && c.x <= max(a.x, b.x) && c.x >= min(a.x, b.x)) return false;
54         else return true;
55     }
56 }
57 int main()
58 {
59     int t;
60     int n,r;
61     scanf("%d", &t);
62     while (t--)
63     {
64         scanf("%d%d", &n, &r);
65         memset(C, 0, sizeof(C));
66         memset(can, false, sizeof(can));
67         int u, v;
68         for(int i=0;i<n;i++)
69         {
70             scanf("%d%d", &pp[i].x, &pp[i].y);
71         }
72         for (int i = 0; i < n; i++)
73         {
74             for (int j = i + 1; j < n; j++)
75             {
76                 bool iscan = true;
77                 for (int k = 0; k < n; k++)
78                 {
79                     if (k!=i&&k!=j&&!isok(pp[i], pp[j], pp[k], r))
80                     {
81                         iscan = false;
82                         break;
83                     }
84                 }
85                 if (iscan)
86                 {
87                     C[i][i]++, C[j][j]++;
88                     C[i][j] = C[j][i] = -1;
89                 }
90             }
91         }
92         int ans = det(C, n);
93         if (ans == 0) printf("-1\n");
94         else printf("%d\n", ans);
95     }
96     return 0;
97 }
View Code

 23、HDU 4408 Minimum Spanning Tree

  题意:给出带权无向图,求最小生成树的个数。

  思路:http://blog.csdn.net/Ramay7/article/details/51899421

    http://www.cnblogs.com/jcf94/p/4071098.html

    Kruskal+Matrix-Tree定理。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <vector>
  6 
  7 using namespace std;
  8 typedef long long ll;
  9 const int N = 105;    //点的个数,点标从1-n
 10 const int M = 1005;  //边的个数
 11 int n, m, u, v, d;
 12 ll MOD;
 13 struct UF
 14 {//并查集
 15     int pre[N];
 16     void init(int n)
 17     {
 18         for (int i = 0; i <= n; i++) pre[i] = i;
 19     }
 20     int Find(int x)
 21     {
 22         if (pre[x] == x)return x;
 23         else
 24         {
 25             int fa = pre[x];
 26             pre[x] = Find(fa);
 27             return pre[x];
 28         }
 29     }
 30     int Union(int x, int y)
 31     {
 32         int xx = Find(x);
 33         int yy = Find(y);
 34         if (xx == yy) return -1;
 35         pre[xx] = yy;
 36         return 1;
 37     }
 38 }a, b;//a,b都是并查集,b为每组边临时使用
 39 
 40 struct eg
 41 {
 42     int u, v, w;
 43     friend bool operator<(const eg &a, const eg&b)
 44     {
 45         return a.w < b.w;
 46     }
 47 }edge[M];
 48 int edgenum;
 49 void add(int u, int v, int d)
 50 {//边从1开始编号
 51     edge[++edgenum].u = u, edge[edgenum].v = v, edge[edgenum].w = d;
 52 }
 53 
 54 bool visit[N];
 55 vector<int> gra[N];
 56 ll C[N][N], deg[N][N];//deg为邻接矩阵,deg[i][j]是点vi和vj之间的边数;C为Kirchhoff矩阵
 57 
 58 ll DET(ll a[][N], int n, ll MOD)
 59 {
 60     int i, j, k;
 61     ll temp = 1, t;
 62     for (i = 0; i < n; i++) for (j = 0; j < n; j++) a[i][j] %= MOD;
 63     for (i = 1; i < n; i++)
 64     {
 65         for (j = i + 1; j < n; j++) while (a[j][i])
 66         {
 67             t = a[i][i] / a[j][i];
 68             for (k = i; k < n; k++)
 69             {
 70                 a[i][k] -= a[j][k] * t;
 71                 a[i][k] %= MOD;
 72             }
 73             for (k = i; k < n; k++)
 74                 swap(a[i][k], a[j][k]);
 75 
 76             temp = -temp;
 77         }
 78         temp = temp*a[i][i] % MOD;
 79     }
 80     return (temp + MOD) % MOD;
 81 }
 82 //假设存在n1条长度为c1的边,n2条长度为c2的边...则Kruskal首先处理c1边的连通性,然后处理c2边的连通性,对于c1边的连通性的处理可能有多种方案,即从n1条边中取出一定数量的边构成最大连通图,但是最终处理完之后的结果对于c2来说是完全一样的。因此算法就出来了,在Kruskal的基础上,使用Matrix-Tree定理处理每个阶段生成树的种数,最后将所有阶段的结果相乘即可。
 83 //在Kruskal的基础上,每完成一个阶段(检查完一个长度),就将所有遍历过的点缩成一个点,然后用Matrix - Tree定理计算该点与下一组点组成的连通图中生成树的个数。最终把每一个阶段的结果相乘即可。
 84 ll cal_MST_count(int n, ll MOD)
 85 {
 86     sort(edge + 1, edge + edgenum + 1);
 87     int prew = edge[1].w;
 88     ll ans = 1;
 89     a.init(n);
 90     b.init(n);
 91     memset(visit, 0, sizeof(visit));
 92     memset(deg, 0, sizeof(deg));
 93     for (int i = 0; i <= n; i++) gra[i].clear();
 94     for (int t = 1; t <= edgenum + 1; t++)
 95     {
 96         if (edge[t].w != prew || t == edgenum + 1)
 97         {//处理完长度为prew的所有边后,即一组边加完,对prew的边连接的每个联通块计算生成树个数
 98             for (int i = 1, k; i <= n; i++)
 99             {
100                 if (visit[i])
101                 {//当前长度的边连接了i节点(祖先)
102                     k = b.Find(i);//b反映新图的连通关系,a还没更新
103                     gra[k].push_back(i);//将i节点压入所属的联通块
104                     visit[i] = 0;//清空vis数组
105                 }
106             }
107             for (int i = 1; i <= n; i++)
108             {//枚举新图每一个连通分量形成的生成树数目
109                 if (gra[i].size())//联通块的点数
110                 {
111                     memset(C, 0, sizeof(C));
112                     int sz = gra[i].size();
113                     for (int j = 0; j <sz; j++)
114                     {//构造该连通块的Kirchhoff矩阵
115                         for (int k = j + 1, x, y; k < sz; k++)
116                         {
117                             x = gra[i][j];
118                             y = gra[i][k];
119                             C[j][k] = C[k][j] = -deg[x][y];
120                             C[j][j] += deg[x][y];
121                             C[k][k] += deg[x][y];
122                         }
123                     }
124                     ans = ans*DET(C, gra[i].size(), MOD) % MOD;
125                     for (int j = 0; j < gra[i].size(); j++) a.pre[gra[i][j]] = i;//缩点,更新并查集a
126                 }
127             }
128             memset(deg, 0, sizeof(deg));
129             for (int i = 1; i <= n; i++)
130             {//连通图缩点,将连通分量并查集的根结点变为一致
131                 b.pre[i] = a.Find(i);
132                 gra[i].clear();
133             }
134             if (t == edgenum + 1) break;
135             prew = edge[t].w;
136         }
137         int x = a.Find(edge[t].u);
138         int y = a.Find(edge[t].v);
139         if (x == y) continue;
140         visit[x] = visit[y] = 1;//标记连通块的祖先
141         b.Union(x, y);//使两个分量在一个联通块,更新连通分量用的并查集b,不更新Kruskal的并查集, 在这一阶段结束才更新, 这是为了使得邻接矩阵代表出连通分量之间的关系
142         deg[x][y]++;//邻接矩阵
143         deg[y][x]++;
144     }
145     if (!edgenum) return 0;
146     for (int i = 2; i <= n; i++)
147         if (b.Find(i) != b.Find(1))//图不连通
148             return 0;
149     return ans;
150 }
151 
152 int main()
153 {
154     while (~scanf("%d%d%d",&n,&m,&MOD))
155     {
156         if (n == 0 && m == 0 && MOD == 0)break;
157         edgenum = 0;
158         while (m--)
159         {
160             scanf("%d%d%d", &u, &v, &d);
161             add(u, v, d);
162         }
163         ll ans = cal_MST_count(n, MOD);
164         printf("%d\n", ans%MOD);
165     }
166     return 0;
167 }
View Code

 24、spoj 104 Highways

  题意:给出无权无向图,求生成树的个数。

  思路:模板题。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N = 20;
 8 typedef long long LL;
 9 LL C[N][N];
10 bool can[N][N];
11 //对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。
12 //所谓n - 1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。
13 LL det(LL c[][N], int n)//生成树计数:Matrix-Tree定理
14 {//c[N][N]为Kirchhoff矩阵,=它的度数矩阵D减去它的邻接矩阵A(A[i][j]=1表示i和j有边相连)
15     LL ret = 1;
16     for (int i = 1; i<n; i++)
17     {
18         for (int j = i + 1; j<n; j++)
19             while (c[j][i])
20             {
21                 LL t = c[i][i] / c[j][i];
22                 for (int k = i; k<n; k++)
23                     c[i][k] = (c[i][k] - c[j][k] * t);
24                 for (int k = i; k<n; k++)
25                     swap(c[i][k], c[j][k]);
26                 ret = -ret;
27             }
28         if (c[i][i] == 0)
29             return 0;
30         ret = ret*c[i][i];
31     }
32     if (ret<0)
33         ret = -ret;
34     return ret;
35 }
36 
37 int main()
38 {
39     int t;
40     scanf("%d", &t);
41     int n, m;
42     while (t--)
43     {
44         scanf("%d%d", &n, &m);
45         memset(C, 0, sizeof(C));
46         memset(can, false, sizeof(can));
47         int u, v;
48         while (m--)
49         {
50             scanf("%d%d", &u, &v);
51             u--;
52             v--;
53             can[u][v] = can[v][u] = true;
54         }
55         for (int i = 0; i < n; i++)
56         {
57             for (int j = i + 1; j < n; j++)
58             {
59                 if (can[i][j])
60                 {
61                     C[i][i]++, C[j][j]++;
62                     C[i][j] = -1, C[j][i] = -1;
63                 }
64             }
65         }
66         printf("%lld\n", det(C, n));
67     }
68     return 0;
69 }
View Code

 

posted @ 2017-08-24 13:50  萌萌的美男子  阅读(261)  评论(0编辑  收藏  举报