• poj 1639 最小度限制生成树


    最小度限制生成树就是给一个图,让求它的最小生成树。找的的最小生成树满足并且点vo的度最大为k

     

    算法流程如下:
    1.将该点(以下用v0表示)从图中删除,将得到p个连通分量。
    2.对每个连通分量求最小生成树。
    3.从每个连通分量中找与v0关联的权值最小的边,与v0相连接,这样将得到v0的最小p度生成树
    4.如果 k  < p 那么这种树是不存在的。
    5.如果 k >= p ,那么考虑构建 p+1度最小生成树,即加入每一条v0相连的且不在当前的树中的边。
    6.显然在第5步将其加入树中 ,必然会存在一个环,那么删掉该环中与v0不关联的权值最大边,将得到加入该边后的最小生成树,且v0p+1度的。
    7.枚举上述 5,6 的边找树权值最小,那么即是p+1度限制的最小生成树。如果p+1度最小生成树的值大于p度最小生成树的话直接输出当前p度的值即可。
    8.重复5.6.7,直到度最小生成树出现。

     

    显然上述算法的1-->3步可以用prim算法实现。复杂度是O(n^2)。而5--->7如果枚举每条边进行增删操作则是O(n^2)的复杂度,总的复杂度达到了O(k*n^2)。在求次小生成树的过程中我们用到了一个path[x][y]记录x -->y的最长边。这里也是用类似的方法设立一个del[]数组,记录v0--->vi路径上的最长边。这样我们每次只要枚举max(del[i] - g[v0][i])即可。注意每次操作后都要更新该连通块的del[]。这样复杂度就降为O(k*n)

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<cmath>
      6 #include<queue>
      7 #include<stack>
      8 #include<string>
      9 #include<vector>
     10 #include<cstdlib>
     11 #include<map>
     12 #include<set>
     13 using namespace std;
     14 #define inf 0x3f3f3f3f
     15 const int maxn = 25;
     16 int m,n,k,g[maxn][maxn],dis[maxn],pre[maxn],ans;
     17 bool vis[maxn],link[maxn][maxn];//link[][]数组来保存在MST中的边
     18 struct node
     19 {
     20     int u,v,len;
     21     node(){}
     22     node(int u, int v, int len):u(u),v(v),len(len){}
     23 }del[maxn];//保存 0到当前节点的路径上的最长边
     24 int kk;
     25 void prim(int st)//求包含st的连通块的MST
     26 {
     27     vis[st] = 1;
     28     memset(pre, -1, sizeof(pre));
     29     for (int i = 1; i < n; ++i)
     30     {
     31         dis[i] = g[st][i];
     32         pre[i] = st;
     33     }
     34     while(1)
     35     {
     36         int tmp = inf, nt = st;
     37         for (int i = 1; i < n; ++i)
     38         {
     39             if (!vis[i] && dis[i] < tmp)
     40             {
     41                 nt = i;
     42                 tmp = dis[i];
     43             }
     44         }
     45         if (st == nt) break;//该连通分量最小生成树完成
     46         if (g[0][nt] < g[0][kk]) kk = nt;//kk保存该连通分量到0距离最近的点
     47         link[pre[nt]][nt] = link[nt][pre[nt]] = 1;//将边加入到MST中
     48         vis[nt] = 1;
     49         ans += dis[nt];
     50         for (int i = 1; i < n; ++i)
     51         {
     52             if (!vis[i] && dis[i] > g[nt][i])
     53             {
     54                 pre[i] = nt;//记录该点前驱
     55                 dis[i] = g[nt][i];
     56             }
     57         }
     58     }
     59 }
     60 void dfs(int cur, int cpre, int u, int v)//修改当前连通分量中到达0的路径上的最大边
     61 {
     62     //cur 当前节点,cpre为当前节点的前驱,(u,v)表示当前节点到0节点的路径上最大边
     63     for (int i = 1; i < n; ++i)
     64     {
     65         if (cpre != i && link[cur][i])
     66         {
     67             if (cpre == -1|| g[cur][i] >= g[u][v])//当前边大于之前保存的最大边
     68             {
     69                 del[i] = node(cur, i, g[cur][i]);
     70                 dfs(i, cur, cur, i);
     71             }
     72             else
     73             {
     74                 del[i] = node(u, v, g[u][v]);
     75                 dfs(i, cur, u, v);
     76             }
     77         }
     78     }
     79 }
     80 void solve()
     81 {
     82     for (int i = 1; i < n; ++i)
     83     {
     84         if (vis[i]) continue;
     85         k--;
     86         kk = i;
     87         prim(i);
     88         ans += g[0][kk];
     89         link[kk][0] = link[0][kk] = 1;
     90         dfs(kk, -1, -1, -1);
     91     }
     92     while(k--)
     93     {
     94         int c = 0, nt = 0;
     95         for (int j = 1; j < n; ++j)//枚举所有节点,找出最大边
     96         {
     97             if (link[0][j] || g[0][j] == inf) continue;
     98             if(c < del[j].len - g[0][j])//找出最大的添删操作
     99             {
    100                 nt = j;
    101                 c = del[j].len - g[0][j];
    102             }
    103         }
    104         if (c == 0) break;
    105         ans -= c;
    106         link[del[nt].u][del[nt].v] = link[del[nt].v][del[nt].u] = false;
    107         link[0][nt] = link[nt][0] = 1;
    108         dfs(nt, 0, -1, -1);//每次操作完成后修改当前连通分量的最长边
    109     }
    110     printf("Total miles driven: %d\n",ans);
    111 }
    112 void init()
    113 {
    114     char s1[20],s2[20];
    115     int w, u, v;
    116     n = 0;
    117     map <string,int> name;
    118     map <string,int>::iterator it1,it2;
    119     name.clear();
    120     name["Park"] = n++;
    121     memset(g, 0x3f, sizeof(g));
    122     memset(vis, 0, sizeof(vis));
    123     memset(link, 0, sizeof(link));
    124     for (int i = 0; i < m; ++i)
    125     {
    126         scanf("%s %s %d", &s1, &s2, &w);
    127         it1 = name.find(s1);
    128         it2 = name.find(s2);
    129         if (it1 != name.end()) u = it1->second;
    130         else
    131         {
    132             name[s1] = n;
    133             u = n++;
    134         }
    135         if (it2 != name.end()) v = it2->second;
    136         else
    137         {
    138             name[s2] = n;
    139             v = n++;
    140         }
    141         if (g[u][v] > w)
    142             g[u][v] = g[v][u] = w;
    143     }
    144     scanf("%d", &k);
    145     ans = 0;
    146 }
    147 int main()
    148 {
    149     while(scanf("%d", &m) != EOF)
    150     {
    151         init();
    152         solve();
    153     }
    154     return 0;
    155 }
  • 相关阅读:
    我的大学,我的梦想
    c++读取lua配置基础类
    无奖调查,你希望我写哪些题材的文章
    无奖调查,你希望我写哪些题材的文章
    lua不同模块调用
    cmake配置c++可调用的文件路径参数
    Java实现 LeetCode 335 路径交叉
    Java实现 LeetCode 335 路径交叉
    Java实现 LeetCode 335 路径交叉
    Java实现 LeetCode 334 递增的三元子序列
  • 原文地址:https://www.cnblogs.com/Missa/p/3005880.html
Copyright © 2020-2023  润新知