• 「长乐集训 2017 Day8」修路 (斯坦纳树)


    题目描述

    村子间的小路年久失修,为了保障村子之间的往来,AAA君决定带领大家修路。

    村子可以看做是一个边带权的无向图GGG, GGG 由 nnn 个点与 mmm 条边组成,图中的点从 1∼n1 sim n1n 进行编号。现在请你选择图中的一些边,使得 ∀1≤i≤dforall 1 leq i leq d1id , iii 号点和 n−i+1n - i + 1ni+1号点可以通过你选择出的那些边连通,并且你要最小化选出的所有边的权值和。请你告诉AAA君这个最小权值和。

    输入格式

    第一行三个整数 nnn, mmm , ddd 表示图中的点数、边数与限制条件。

    接下来 mmm 行每行三个整数 uiu_iui​​, viv_ivi​​ , wiw_iwi​​ 表示一条连接 (ui,vi)(u_i, v_i)(ui​​,vi​​) 的权值为叫的无向边。

    输出格式

    仅一行一个整数表示答案。若无解输出 −1-11 .

    样例

    样例输入

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

    样例输出

    9
      1 #include <bits/stdc++.h>
      2 
      3 using namespace std;
      4 const int maxn = 1e4+500;
      5 const int inf = 1<<29;
      6 int fa[maxn];
      7 int n,m,d,ST;
      8 int s[maxn];
      9 bool inq[maxn][1<<8];
     10 int f[maxn][1<<8];//f[i][j]根节点为i,连同状态为j的最小生成树
     11 int g[1<<8];// g 连同状态为j的最小生成树
     12 int ehead[maxn],ecnt;
     13 queue<pair<int,int> >que;
     14 inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
     15 struct edge
     16 {
     17     int u,v,w,next;
     18 }edg[maxn*2];
     19 bool upd(int &u,int v)
     20 {
     21     return u>v?(u=v,1):0;
     22 }
     23 void add (int u,int v,int w)
     24 {
     25     edg[++ecnt] = (edge){u,v,w,ehead[u]};
     26     ehead[u]=ecnt;
     27     edg[++ecnt] = (edge){v,u,w,ehead[v]};
     28     ehead[v]=ecnt;
     29 
     30 }
     31 int findd (int x)
     32 {
     33     if (fa[x]==x) return x;
     34     else return fa[x]=findd(fa[x]);
     35 }
     36 void Union (int x,int y)
     37 {
     38     int fx = findd(x),fy = findd(y);
     39     if (fx==fy) return ;
     40     else {
     41         fa[fx] = fy;
     42         return ;
     43     }
     44 }
     45 void spfa ()
     46 {
     47     while (!que.empty()){
     48         pair<int,int> h = que.front();
     49         que.pop();
     50         int u = h.first,t = h.second;
     51         inq[u][t] = false;
     52         for (int j=ehead[u];j;j=edg[j].next){
     53             int v = edg[j].v,k = s[v]|t;
     54             if (upd(f[v][k],f[u][t]+edg[j].w)&&k==t){
     55                 if (!inq[v][k]){
     56                     que.push(make_pair(v,k));
     57                     inq[v][k] = true;
     58                 }
     59             }
     60         }
     61     }
     62 }
     63 bool check (int j)
     64 {
     65     for (int i=0;i<d;++i){
     66         int a = (j>>i)&1;
     67         int b = (j>>(i+d))&1;
     68         if (a!=b) return false;
     69     }
     70     return true;
     71 }
     72 void init ()
     73 {
     74     ecnt = 0;
     75     for (int i=0;i<maxn;++i)
     76         fa[i] = i;
     77     memset(inq,false,sizeof inq);
     78 }
     79 int main()
     80 {
     81     //freopen("road10.in","r",stdin);
     82     freopen("road.in","r",stdin);
     83     freopen("road.out","w",stdout);
     84     read(n);read(m);read(d);
     85     //scanf("%d%d%d",&n,&m,&d);
     86     init();
     87     ST=(1<<(2*d))-1;
     88     for (int i=1;i<=n;++i)
     89         for (int j=1;j<=ST;++j)
     90             f[i][j] = inf;
     91     for (int i=0;i<m;++i){
     92         int u,v,w;
     93         read(u);read(v);read(w);
     94         add(u,v,w);
     95         Union(u,v);
     96     }
     97     for (int i=1;i<=d;++i){
     98         s[i] = 1<<(i-1);
     99         f[i][s[i]] = 0;
    100         s[n-i+1] = 1<<(i-1+d);
    101         f[n-i+1][s[n-i+1]] = 0;
    102         if (findd(i)!=findd(n-i+1)){
    103             printf("-1
    ");
    104             return 0;
    105         }
    106     }
    107     for (int j=0;j<=ST;++j){
    108         for (int i=1;i<=n;++i){
    109             for (int k=j;k>0;k=((k-1)&j)){//枚举j的子集k
    110                 upd(f[i][j],f[i][k|s[i]]+f[i][(j^k)|s[i]]);//当前根节点为i 更新
    111             }
    112         }
    113         for (int i=1;i<=n;++i){
    114             if (f[i][j]<inf){
    115                 inq[i][j] = true;
    116                 que.push(make_pair(i,j));
    117             }
    118         }
    119         spfa();
    120     }
    121     for (int j=1;j<=ST;++j){
    122         g[j] = inf;
    123         if (!check(j)) continue ;
    124         for (int i=1;i<=n;++i)
    125             upd(g[j],f[i][j]);
    126     }
    127     for (int j=1;j<=ST;++j){
    128         for (int k=j;k>0;k=(k-1)&j){
    129             upd(g[j],g[k]+g[k^j]);
    130         }
    131     }
    132     printf("%d
    ",g[ST]);
    133     return 0;
    134 }

    转载:http://blog.csdn.net/gzh1992n/article/details/9119543

    1. 什么是斯坦纳树?

           斯坦纳树问题是组合优化学科中的一个问题。将指定点集合中的所有点连通,且边权总和最小的生成树称为最小斯坦纳树(Minimal Steiner Tree),其实最小生成树是最小斯坦纳树的一种特殊情况。而斯坦纳树可以理解为使得指定集合中的点连通的树,但不一定最小。

    2. 如何求解最小斯坦纳树?

          可以用DP求解,dp[i][state]表示以i为根,指定集合中的点的连通状态为state的生成树的最小总权值。

          转移方程有两重:

          第一重,先通过连通状态的子集进行转移。

          dp[i][state]=min{ dp[i][subset1]+dp[i][subset2] } 

          枚举子集的技巧可以用 for(sub=(state-1)&state;sub;sub=(sub-1)&state)。

          第二重,在当前枚举的连通状态下,对该连通状态进行松弛操作。

          dp[i][state]=min{ dp[i][state], dp[j][state]+e[i][j] }

          为什么只需对该连通状态进行松弛?因为更后面的连通状态会由先前的连通状态通过第一重转移得到,所以无需对别的连通状态松弛。松弛操作用SPFA即可。

          复杂度 O(n*3^k+cE*2^k)

          c为SPFA复杂度中的常数,E为边的数量,但几乎达不到全部边的数量,甚至非常小。3^k来自于子集的转移sum{C(i,n)*2^i} (1<=i<=n),用二项式展开求一下和。

     1 /*
     2  *  Steiner Tree:求,使得指定K个点连通的生成树的最小总权值
     3  *  st[i] 表示顶点i的标记值,如果i是指定集合内第m(0<=m<K)个点,则st[i]=1<<m 
     4  *  endSt=1<<K
     5  *  dptree[i][state] 表示以i为根,连通状态为state的生成树值
     6  */
     7 #define CLR(x,a) memset(x,a,sizeof(x))
     8 
     9 int dptree[N][1<<K],st[N],endSt;
    10 bool vis[N][1<<K];
    11 queue<int> que;
    12 
    13 int input()
    14 {
    15    /*
    16     *    输入,并且返回指定集合元素个数K
    17     *    因为有时候元素个数需要通过输入数据处理出来,所以单独开个输入函数。
    18     */
    19 }
    20 
    21 void initSteinerTree()
    22 {
    23     CLR(dptree,-1);
    24     CLR(st,0);
    25     for(int i=1;i<=n;i++) CLR(vis[i],0);
    26     endSt=1<<input();
    27     for(int i=1;i<=n;i++)
    28         dptree[i][st[i]]=0;
    29 }
    30 
    31 void update(int &a,int x)
    32 {
    33     a=(a>x || a==-1)? x : a;
    34 }
    35 
    36 void SPFA(int state)
    37 {
    38     while(!que.empty()){
    39         int u=que.front();
    40         que.pop();
    41         vis[u][state]=false;
    42         for(int i=p[u];i!=-1;i=e[i].next){
    43             int v=e[i].vid;
    44             if(dptree[v][st[v]|state]==-1 || 
    45                 dptree[v][st[v]|state]>dptree[u][state]+e[i].w){
    46 
    47                 dptree[v][st[v]|state]=dptree[u][state]+e[i].w;
    48                 if(st[v]|state!=state || vis[v][state]) 
    49                     continue; //只更新当前连通状态
    50                 vis[v][state]=true;
    51                 que.push(v);
    52             }
    53         }
    54     }
    55 }
    56 
    57 void steinerTree()
    58 {
    59     for(int j=1;j<endSt;j++){
    60         for(int i=1;i<=n;i++){
    61             if(st[i] && (st[i]&j)==0) continue;
    62             for(int sub=(j-1)&j;sub;sub=(sub-1)&j){
    63                 int x=st[i]|sub,y=st[i]|(j-sub);
    64                 if(dptree[i][x]!=-1 && dptree[i][y]!=-1)
    65                     update(dptree[i][j],dptree[i][x]+dptree[i][y]);
    66             }
    67             if(dptree[i][j]!=-1) 
    68                 que.push(i),vis[i][j]=true;
    69         }
    70         SPFA(j);
    71     }
    72 }
     
     
  • 相关阅读:
    Android开发效率—Eclipse快捷键
    Android学习笔记之AndroidManifest.xml文件解析
    Android开发环境的安装 Eclipse
    运用CodeSmith Studio实现C#项目构架
    使用C#开发ActiveX控件(新) 转 http://www.cnblogs.com/yilin/p/csharp-activex.html
    C# winFrom 制作、打包、签名、发布Activex全过程
    C# Server.MapPath()
    PowerDesigner导出表到word
    powerdesigner 15 如何导出sql schema
    PowerDesigner15使用时的十五个问题
  • 原文地址:https://www.cnblogs.com/agenthtb/p/7689660.html
Copyright © 2020-2023  润新知