zoukankan      html  css  js  c++  java
  • 生成树专题

    生成树专题地址

    uvalive3887

    给定一个带权的无向图,求得一棵最小生成树,是的树中的最大边权-最小边权的差值最小

    分析:当确定一个最小边时(其他的边的权值都比他大),那么之后按照kruskal算法得到的最小生成树,

    此时得到的最小生成树的最大权值也肯定是最小的,因为是kruskal是按照贪心来选边的。

    所以只要不断的枚举是哪条边作为最小边,然后生成最小生成树,记录所有差值中最小的一个就是答案。

    时间复杂度是O(m*m*logm)

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include <algorithm>
      5 #include <iostream>
      6 #include <queue>
      7 #include <stack>
      8 #include <vector>
      9 #include <map>
     10 #include <set>
     11 #include <string>
     12 #include <math.h>
     13 using namespace std;
     14 #pragma warning(disable:4996)
     15 #pragma comment(linker, "/STACK:1024000000,1024000000")
     16 typedef long long LL;                   
     17 const int INF = 1<<30;
     18 /*
     19 最大边的权值减去最小边的权值      要最小的生成树, 
     20 即树的边权尽可能相近
     21 
     22 枚举最小边的权值,然后生成生成树 , 然后计算权值之差,  取取最小的那一个
     23 时间复杂度是m * m * logm  ,
     24 
     25 
     26 因为生成树是
     27 */
     28 const int N = 350 + 10;
     29 struct Edge
     30 {
     31     int u, v, dis;
     32     bool operator<(const Edge&rhs)
     33     {
     34         return dis < rhs.dis;
     35     }
     36 }g[N*N];
     37 int fa[N];
     38 int find(int x)
     39 {
     40     if (x == fa[x])
     41         return x;
     42     return fa[x] = find(fa[x]);
     43 }
     44 int kruskal(int n, int j ,int m)
     45 {
     46     for (int i = 0; i <= n; ++i)
     47     {
     48         fa[i] = i;
     49     }
     50     int cnt = 0, ret = 0;
     51     for (int i = j; i < m; ++i)
     52     {
     53         int fx = find(g[i].u);
     54         int fy = find(g[i].v);
     55         if (fx != fy)
     56         {
     57             fa[fx] = fy;
     58             if (cnt == 0)
     59                 ret = g[i].dis;
     60             if (cnt != 0 && g[i].dis - ret > ans)
     61                 return -1;
     62             cnt++;
     63             if (cnt == n - 1)
     64                 ret = g[i].dis - ret;
     65         }
     66     }
     67     if (cnt == n - 1)
     68         return ret;
     69     return  -1;
     70 }
     71 
     72 
     73 int main()
     74 {
     75     //freopen("d:/in.txt", "r", stdin);
     76     int n, m;
     77     while (scanf("%d",&n),n)
     78     {
     79         scanf("%d", &m);
     80         for (int i = 0; i < m; ++i)
     81             scanf("%d%d%d", &g[i].u, &g[i].v, &g[i].dis);
     82         sort(g, g + m);
     83         int ans = kruskal(n, 0 ,m);
     84         if (ans == -1)
     85             puts("-1");
     86         else
     87         {
     88             for (int i = 1; i < m; ++i)
     89             {
     90                 int ret = kruskal(n, i, m);
     91                 if (m - i < n - 1)
     92                     break;
     93                 if (ret != -1)
     94                     ans = min(ans, ret);
     95             }
     96             printf("%d
    ", ans);
     97         }
     98     }
     99     return 0;
    100 }
    View Code

    uvalive10600

    给定一个带权无向图,求最小生成树和次小生成树

    分析:只要在生成最小生成树的同时,存储这课树, 然后dfs求出树上任意两点的最大边权maxCost,

    然后枚举哪条边用来替换树中的边, 然后会获得一个权值, 找到所有权值中最小的就是次小生成树了

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include <algorithm>
      5 #include <iostream>
      6 #include <queue>
      7 #include <stack>
      8 #include <vector>
      9 #include <map>
     10 #include <set>
     11 #include <string>
     12 #include <math.h>
     13 using namespace std;
     14 #pragma warning(disable:4996)
     15 #pragma comment(linker, "/STACK:1024000000,1024000000")
     16 typedef long long LL;                   
     17 const int INF = 1<<30;
     18 /*
     19 
     20 */
     21 const int N = 100 + 10;
     22 struct Edge
     23 {
     24     int u, v, dis;
     25     bool operator<(const Edge&rhs)const
     26     {
     27         return dis < rhs.dis;
     28     }
     29 }edges[N*N];
     30 bool vis[N*N];
     31 bool mark[N];
     32 vector<Edge> g[N];
     33 vector<int> st;
     34 int fa[N];
     35 int maxCost[N][N];
     36 int find(int x)
     37 {
     38     if (x == fa[x])
     39         return x;
     40     return fa[x] = find(fa[x]);
     41 }
     42 int kruskal(int n, int m)
     43 {
     44     int ret = 0, cnt = 0;
     45     for (int i = 1; i <= n; ++i)
     46     {
     47         fa[i] = i;
     48         g[i].clear();
     49     }
     50     for (int i = 0; i < m; ++i)
     51     {
     52         int fx = find(edges[i].u);
     53         int fy = find(edges[i].v);
     54         if (fx != fy)
     55         {
     56             fa[fx] = fy;
     57             vis[i] = true;
     58             g[edges[i].u].push_back(edges[i]);
     59             swap(edges[i].u, edges[i].v);
     60             g[edges[i].u].push_back(edges[i]);
     61             swap(edges[i].u, edges[i].v);
     62             ret += edges[i].dis;
     63             cnt++;
     64             if (cnt == n - 1)
     65                 return ret;
     66         }
     67     }
     68     return ret;
     69 }
     70 
     71 void dfs(int u, int fa, int dis)
     72 {
     73     mark[u] = true;
     74     if (fa!=-1)
     75     for (int i = 0; i < st.size(); ++i)
     76     {
     77         int x = st[i];
     78         maxCost[u][x] = maxCost[x][u] = max(maxCost[x][fa], dis);
     79     }
     80     st.push_back(u);
     81     for (int i = 0; i < g[u].size(); ++i)
     82     {
     83         int v = g[u][i].v;
     84         if (mark[v]) continue;
     85         dfs(v, u, g[u][i].dis);
     86     }
     87 }
     88 int main()
     89 {
     90     int t, n, m;
     91     scanf("%d", &t);
     92     while (t--)
     93     {
     94         scanf("%d%d", &n, &m);
     95         for (int i = 0; i < m; ++i)
     96         {
     97             scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].dis);
     98             vis[i] = false;
     99         }
    100         sort(edges, edges + m);
    101         int ans1 = kruskal(n, m);
    102         int ans2 = INF;
    103         st.clear();
    104         memset(mark, 0, sizeof(mark));
    105         dfs(1, -1, 0);
    106         for (int i = 0; i < m; ++i)
    107             if (!vis[i])
    108                 ans2 = min(ans2, ans1 - maxCost[edges[i].u][edges[i].v] + edges[i].dis);
    109             printf("%d %d
    ", ans1, ans2);
    110     }
    111     return 0;
    112 }
    View Code

    hdu2121

    给定一个带权的有向图, 问该图存不存在最小树形图;

    分析:并不知道是哪一个点做根结点。 如果暴力枚举哪个点作为根结点的话,会超时。

      更好的方法是设置一个虚根,让它指向所有的点,且权值设为所有边的权值之和+1(设为sum).

      那么以该虚根求出最小生成树后,最小生成树的权值-sum >= sum 表示原图的最小树形图不存在

      如果小于,那么就说明原图的最小树形图的权值是:最小树形图的权值-sum

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include <algorithm>
      5 #include <iostream>
      6 #include <queue>
      7 #include <stack>
      8 #include <vector>
      9 #include <map>
     10 #include <set>
     11 #include <string>
     12 #include <math.h>
     13 using namespace std;
     14 #pragma warning(disable:4996)
     15 #pragma comment(linker, "/STACK:1024000000,1024000000")
     16 typedef long long LL;                   
     17 const int INF = 1<<30;
     18 /*
     19 
     20 */
     21 struct Node
     22 {
     23     int u, v, dis;
     24 }a[11111];
     25 int in[1111], vis[1111], id[1111], pre[1111];
     26 pair<int,int> directed_MST(int root, int n, int m)
     27 {
     28     pair<int, int> ret;
     29     ret.first = 0;
     30     while (true)
     31     {
     32         for (int i = 0; i < n; ++i)
     33             in[i] = INF;
     34         for (int i = 0; i < m; ++i)
     35         {
     36             //自环有什么影响??
     37             //如果有自环,且权值足够小, 那么就不会选择别的点指向自己的边
     38             //那么就要循环到别的点指向自己的边的权值足够小时,才会被选择
     39             if (a[i].u!=a[i].v &&a[i].dis < in[a[i].v])
     40             {
     41                 in[a[i].v] = a[i].dis;
     42                 pre[a[i].v] = a[i].u;
     43                 if (a[i].u == root)
     44                     ret.second = i;//不能直接存储点,应该点会被重新编号
     45             }
     46         }
     47         for (int i = 0; i < n; ++i)
     48         if (i != root &&in[i] == INF){
     49             ret.first = -1;
     50             return ret;
     51         }
     52         in[root] = 0;
     53         for (int i = 0; i < n; ++i)
     54         {
     55             vis[i] = id[i] = -1;
     56         }
     57         int cnt = 0;
     58         for (int i = 0; i < n; ++i)
     59         {
     60             ret.first += in[i];
     61             int v = i;
     62             while (v != root && vis[v] != i && id[v] == -1)
     63             {
     64                 vis[v] = i;
     65                 v = pre[v];
     66             }
     67             if (v != root && id[v] == -1)
     68             {
     69                 for (int u = pre[v]; u != v; u = pre[u])
     70                     id[u] = cnt;
     71                 id[v] = cnt++;
     72             }
     73         }
     74         if (cnt == 0) break;
     75         for (int i = 0; i < n;++i)
     76         if (id[i] == -1) id[i] = cnt++;
     77         for (int i = 0; i < m; ++i)
     78         {
     79             int v = a[i].v;
     80             a[i].u = id[a[i].u];
     81             a[i].v = id[a[i].v];
     82             if (a[i].u != a[i].v)
     83                 a[i].dis -= in[v];
     84         }
     85         root = id[root];
     86         n = cnt;
     87     }
     88     return ret;
     89 }
     90 int main()
     91 {
     92     int n, m, sum;
     93     while (scanf("%d%d", &n, &m) != EOF)
     94     {
     95         sum = 0;
     96         for (int i = 0; i < m; ++i)
     97         {
     98             scanf("%d%d%d", &a[i].u, &a[i].v, &a[i].dis);
     99             sum += a[i].dis;
    100         }
    101         sum++;
    102         for (int i = m; i < n + m; ++i)
    103         {
    104             a[i].u = n;
    105             a[i].v = i - m;
    106             a[i].dis = sum;
    107         }
    108         pair<int, int> ret = directed_MST(n, n + 1, n + m);
    109         if (ret.first - sum >= sum)
    110             puts("impossible");
    111         else
    112             printf("%d %d
    ", ret.first - sum, ret.second - m);
    113         puts("");
    114     }
    115     return 0;
    116 }
    View Code

    spoj Find The Determinant III

    给一个矩阵, 要我们求矩阵的行列式模p之后的值。

    只要将矩阵转为为上三角矩阵,那么行列式的值就是对角线上的元素相乘,再乘以1或者-1即可

    要将矩阵转为上三角, 那么问题是给定两行数,如果将一行数字的第一个数字变为0,  我们可以运用欧几里得的性质,让大的数字不断减去小的数字, 那么肯定有一个数字会变成0

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <algorithm>
     5 #include <iostream>
     6 #include <queue>
     7 #include <stack>
     8 #include <vector>
     9 #include <map>
    10 #include <set>
    11 #include <string>
    12 #include <math.h>
    13 using namespace std;
    14 #pragma warning(disable:4996)
    15 #pragma comment(linker, "/STACK:1024000000,1024000000")
    16 typedef long long LL;                   
    17 const int INF = 1<<30;
    18 /*
    19 
    20 */
    21 LL g[222][222];
    22 LL solve(int n, int MOD)
    23 {
    24     LL ret = 1;
    25     for (int i = 1; i <= n; ++i)
    26     {
    27         for (int j = i + 1; j <= n; ++j)
    28         {
    29             while (g[j][i])
    30             {
    31                 //下面就是欧几里得过程了, 让大的数字减去小的,然后交换
    32                 LL t = g[i][i] / g[j][i];
    33                 for (int k = i; k <= n; ++k)
    34                     g[i][k] = (g[i][k] - g[j][k] * t) % MOD;
    35                 for (int k = i; k <= n; ++k)
    36                     swap(g[i][k], g[j][k]);
    37                 ret = -ret;
    38             }
    39         }
    40         if (g[i][i] == 0) return 0;
    41         ret = ret * g[i][i] % MOD;
    42     }
    43 
    44     return (ret%MOD + MOD) % MOD;
    45 }
    46 int main()
    47 {
    48     int n, p;
    49     while (scanf("%d%d", &n, &p) != EOF)
    50     {
    51         for (int i = 1; i <= n; ++i)
    52         for (int j = 1; j <= n; ++j)
    53             scanf("%lld", &g[i][j]);
    54         printf("%lld
    ", solve(n, p));
    55     }
    56     return 0;
    57 }
    View Code

    spoj Highways  生成树计数

    给定一个无向图,问生成树的个数有多少个

    运用matrix_tree定理即可。 

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <algorithm>
     5 #include <iostream>
     6 #include <queue>
     7 #include <stack>
     8 #include <vector>
     9 #include <map>
    10 #include <set>
    11 #include <string>
    12 #include <math.h>
    13 using namespace std;
    14 #pragma warning(disable:4996)
    15 typedef long long LL;                   
    16 const int INF = 1<<30;
    17 /*
    18 给定一个无向图, 问与多少中删边的方式,形成一棵树
    19 C = A  - G
    20 */
    21 const int N = 105;
    22 LL C[N][N];
    23 int degree[N];
    24 LL det(int n)
    25 {
    26     LL ret = 1;
    27     for (int i = 1; i < n; ++i)
    28     {
    29         for (int j = i + 1; j < n; ++j)
    30         {
    31             while (C[j][i])
    32             {
    33                 LL t = C[i][i] / C[j][i];
    34                 for (int k = i; k < n; ++k)
    35                     C[i][k] = C[i][k] -  C[j][k] * t;
    36                 for (int k = i; k < n; ++k)
    37                     swap(C[i][k], C[j][k]);
    38             }
    39         }
    40         if (C[i][i] == 0)return 0;
    41         ret = ret *C[i][i];
    42     }
    43     if (ret < 0) ret = -ret;;
    44     return ret;
    45 }
    46 
    47 
    48 int main()
    49 {
    50     int t, n, m;
    51     int u, v;
    52     scanf("%d", &t);
    53     while (t--)
    54     {
    55         memset(degree, 0, sizeof(degree));
    56         memset(C, 0, sizeof(C));
    57         scanf("%d%d", &n, &m);
    58         for (int i = 0; i < m; ++i)
    59         {
    60             scanf("%d%d", &u, &v);
    61             C[u][v] = C[v][u] = -1;
    62             degree[u]++;
    63             degree[v]++;
    64         }
    65         for (int i = 1; i <= n; ++i)
    66             C[i][i] = degree[i];
    67         printf("%lld
    ", det(n));
    68     }
    69     return 0;
    70 }
    View Code

    hdu4408 最小生成树计数

    给定一个带权无向图, 问最小生成树的个数

    因为带权,  所以需要变通,然后使用matrix_tree定理。  如

    可以将kruskal处理相同权值的边的过程看成一个阶段,   那么这个阶段就是求一棵树的过程, 这个阶段结束后, 这个阶段的结果就缩成了一个点。

    所以只要将每个阶段都运用matrix_tree定理, 然后每个阶段的结果相乘, 那么就得到答案了。

    这题有坑,  n=1的时候, 是输出0,而不是1。 被坑了一晚上

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include <algorithm>
      5 #include <iostream>
      6 #include <queue>
      7 #include <stack>
      8 #include <vector>
      9 #include <map>
     10 #include <set>
     11 #include <string>
     12 #include <math.h>
     13 using namespace std;
     14 #pragma warning(disable:4996)
     15 #pragma comment(linker, "/STACK:1024000000,1024000000")
     16 typedef __int64 LL;
     17 const int INF = 1 << 30;
     18 /*
     19 对每一个阶段进行matrix_tree定理  然后把所有阶段的结果相乘
     20 */
     21 const int N = 111;
     22 const int M = 1111;
     23 int n, m, p;
     24 struct Node
     25 {
     26     int u, v, dis;
     27     bool operator<(const Node&rhs)const
     28     {
     29         return dis < rhs.dis;
     30     }
     31 }edges[M];
     32 LL f1[N], f2[N]
     33 /*
     34 f1用来缩点使用, 如果f1[i]相等, 说明这这些点都缩成了以父亲为编号的点
     35 */
     36 LL G[N][N], C[N][N];
     37 vector<int> g[N];
     38 LL vis[N];
     39 int find(int x, LL f[])
     40 {
     41     if (x == f[x])
     42         return x;
     43     return find(f[x], f);
     44 }
     45 
     46 LL det(LL a[][N], int n)
     47 {
     48     int ret = 1;
     49     for (int i = 1; i < n; ++i)
     50     {
     51         for (int j = i + 1; j < n; ++j)
     52         {
     53             while (a[j][i])
     54             {
     55                 int t = a[i][i] / a[j][i];
     56                 for (int k = i; k < n; ++k)
     57                     a[i][k] = (a[i][k] - a[j][k] * t) % p;
     58                 for (int k = i; k < n; ++k)
     59                     swap(a[i][k], a[j][k]);
     60                 ret = -ret;
     61             }
     62         }
     63         if (a[i][i] == 0) return 0;
     64         ret = (ret*a[i][i]) % p;
     65     }
     66     return (ret + p) % p;
     67 }
     68 void solve()
     69 {
     70     
     71     LL ans = 1;
     72     LL dis = edges[0].dis;
     73     int fx, fy;
     74     for (int i = 1; i <= n; ++i)
     75     {
     76         f1[i] = f2[i] = i;
     77         vis[i] = false;
     78     }
     79     for (int i = 0; i <= m; ++i)
     80     {
     81         if (edges[i].dis != dis || i == m)
     82         {
     83             for (int j = 1; j <= n; ++j)
     84             {
     85                 if (vis[j])//找到这一阶段权值相等的边
     86                 {
     87                     g[find(j, f2)].push_back(j);
     88                     vis[j] = false;
     89                 }
     90             }
     91             for (int j = 1; j <= n; ++j)
     92             {
     93                 if (g[j].size() > 1)//枚举每一个连通分量,至少有两个点才说明有边
     94                 {
     95                     int len = g[j].size();
     96                     for (int a = 0; a < len; ++a)
     97                     for (int b = 0; b < len; ++b)
     98                         C[a][b] = 0;
     99                     for (int a = 0; a < len; ++a)
    100                     for (int b = a + 1; b < len; ++b)
    101                     {
    102                         fx = g[j][a];
    103                         fy = g[j][b];
    104                         C[a][b] = (C[b][a] -= G[fx][fy]);
    105                         C[a][a] += G[fx][fy];
    106                         C[b][b] += G[fx][fy];
    107                     }
    108                     LL ret = det(C, len);
    109                     ans = (ans*ret) % p;
    110 
    111                     //缩点
    112                     for (int a = 0; a < len; ++a)
    113                         f1[g[j][a]] = j;
    114                 }
    115 
    116             }
    117             for (int j = 1; j <= n; ++j)
    118                 g[j].clear();
    119             if (i == m)break;
    120 
    121             dis = edges[i].dis;
    122         }
    123 
    124 
    125         //找到不在一个集合里面的,权值相等的所有边。
    126         fx = find(edges[i].u, f1);
    127         fy = find(edges[i].v, f1);
    128         if (fx == fy) continue;
    129         //这些边用vis和并查集来存储
    130         vis[fx] = vis[fy] = true;
    131         f2[find(fx, f2)] = find(fy, f2);
    132         G[fx][fy]++;
    133         G[fy][fx]++;
    134 
    135     }
    136     bool flag = true;
    137     if ( m == 0) flag = false;//这里最坑了。 n=1的时候,是输出0,而不是输出1
    138     for (int i = 1; i <= n; ++i)
    139         f2[i] = find(i, f2);
    140     for (int i = 2; i <= n&&flag; ++i)
    141     if (f2[i] != f2[i - 1])
    142         flag = false;
    143     if (flag)
    144         printf("%I64d
    ", ans);
    145     else
    146         printf("0
    ");
    147 }
    148 int main()
    149 {
    150     while (scanf("%d%d%d", &n, &m, &p), n + m + p)
    151     {
    152         memset(G, 0, sizeof(G));
    153         for (int i = 0; i < m; ++i)
    154             scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].dis);
    155         sort(edges, edges + m);
    156         solve();
    157     }
    158     return 0;
    159 }
    View Code

    Find The Determinant III

  • 相关阅读:
    运维自动化轻量级工具pssh
    Linux下的tar压缩解压缩命令详解
    [shell] while read line 与for循环的区别
    Linux sed命令
    [转]linux awk命令详解
    Centos7上部署openstack ocata配置详解
    自动化运维工具——puppet详解(一)
    OpenStack 初探(一) -- All-In-One模式部署(初学OpenStack必备)
    shell中的重定向(输入输出)
    vim批量注释和反注释快捷键
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4688135.html
Copyright © 2011-2022 走看看