• 生成树专题


    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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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.
    ", 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
    ",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
    ", 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
    ", 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
    ", 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
    ", inians);
    123         else printf("Not Unique!
    ");
    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
    ", 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
    ", ans*100);
    96         else printf("oh!
    ");
    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
    ", 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
    ", 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
    ", 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
    ", Case++, len2);
    149         }
    150         else printf("Case #%d : No second way
    ", 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
    ");
    107         else printf("%.2lf
    ", 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!
    ", Case++);
    111         else printf("Case #%d: %d
    ", 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
    
    ");
    106         else
    107             printf("%d %d
    
    ", 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
    ");
    126         else
    127             printf("%d
    ", 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
    ", 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
    ", 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
    ");
    94         else printf("%d
    ", 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
    ", 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
    ", det(C, n));
    67     }
    68     return 0;
    69 }
    View Code
  • 相关阅读:
    Spring4+SpringMVC+MyBatis登录注册详细
    Spring MVC登录注册以及转换json数据
    MyBatis+mysql查询和添加数据
    html5中的选择器
    倒影(转)
    bi包
    函数作用域
    节点开始
    window.onload中失效的问题
    Node.js简介
  • 原文地址:https://www.cnblogs.com/ivan-count/p/7422637.html
Copyright © 2020-2023  润新知