zoukankan      html  css  js  c++  java
  • 「长乐集训 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 }
     
     
  • 相关阅读:
    Two Sum II
    Subarray Sum
    Intersection of Two Arrays
    Reorder List
    Convert Sorted List to Binary Search Tree
    Remove Duplicates from Sorted List II
    Partition List
    Linked List Cycle II
    Sort List
    struts2结果跳转和参数获取
  • 原文地址:https://www.cnblogs.com/agenthtb/p/7689660.html
Copyright © 2011-2022 走看看