hdu 3311 steiner tree / 状态压缩spfa

hdu 3311 steiner tree / 状态压缩spfa
http://acm.hdu.edu.cn/showproblem.php?pid=3311

给定6个基础点,和1000个辅助点,以及无向边若干。
求一棵代价最小的生成子树,使得6个基础点连通。
http://en.wikipedia.org/wiki/Steiner_tree_problem

方法一:
状态DP。
一棵树,实际上可以看成是由两棵子树拼接而成,或者一棵树扩展一条边筛选。而要完整地表示一棵子树,需要记录它的根,和对6个基础点的覆盖状态。
这样求一棵大树,可以枚举接合点,以及两个子树的覆盖状态。
若dp[i][j],i表示接合点,j表示覆盖状态,那么dp[i][j]=min{dp[i][k]+dp[i][j^k]}。
直接扩展一条边, 可以在保持j不变的情况下, 优先队列广搜, 大大降低复杂度.

方法二:
spfa。
dp[i][j]意义同上。一个点(i,j),对每个邻接点i',枚举那一头的状态j',然后用dp[i][j]+dp[i'][j']+way[i][i']去更新dp[i][j|j']和dp[i'][j|j']。

ps. topcoder srm 470 div1 level3是此题升级版.

  1  #include  < string >
  2  #include  < vector >
  3  #include  < list >
  4  #include  < map >
  5  #include  < queue >
  6  #include  < stack >
  7  #include  < set >
  8  #include  < iostream >
  9  #include  < sstream >
 10  #include  < numeric >
 11  #include  < algorithm >
 12  #include  < cmath >
 13  #include  < cctype >
 14  #include  < cstdlib >
 15  #include  < cstdio >
 16  #include  < cstring >
 17  using   namespace  std;
 18 
 19  #define  CLR(x) memset((x),0,sizeof(x))
 20  #define  SET(x,y) memset((x),(y),sizeof(x))
 21  #define  REP(i,x) for(int i=0;i<(x);i++)
 22  #define  FOR(i,x,y) for(int i=(x);i<(y);i++)
 23  #define  VI vector<int> 
 24  #define  PB(i,x) (i).push_back(x)
 25  #define  MP(x,y) make_pair((x),(y))
 26 
 27  struct  EDGE{
 28       int  v,e,d;
 29  }edg[ 20000 ];
 30  int  ecnt, gg[ 1006 ];
 31 
 32  struct  HEAP{
 33       int  v,d;
 34       void   set ( int  nv,  int  nd){v = nv;d = nd;}
 35  }hp[ 1006 * ( 1 << 6 ) * 10 ];
 36  int  sz;
 37  bool  vis[ 1006 ];
 38 
 39  int  dp[ 1006 ][ 1 << 6 ];
 40  int  N, M, P;
 41 
 42  bool  cmp( const  HEAP  & a,  const  HEAP  & b)
 43  return  a.d > b.d; }
 44 
 45  void  addedge( int  u,  int  v,  int  d)
 46  {
 47      edg[ecnt].v = v; edg[ecnt].d = d; edg[ecnt].e = gg[u]; gg[u] = ecnt ++ ;
 48      edg[ecnt].v = u; edg[ecnt].d = d; edg[ecnt].e = gg[v]; gg[v] = ecnt ++ ;
 49  }
 50 
 51  int  steiner()
 52  {
 53       int  up  =   1 << (N + 1 );
 54      SET(dp, - 1 );
 55      REP(i,N + 1 ) dp[i][ 1 << i] = 0 ;
 56      FOR(i,N + 1 ,N + M + 1 ) dp[i][ 0 ] = 0 ;
 57      REP(i,up){
 58          REP(j,N + M + 1 ){
 59               for ( int  k = i; k > 0 ; k = (k - 1 ) & i){
 60                   if (dp[j][k] !=- 1   &&  dp[j][i ^ k] !=- 1 )
 61                       if (dp[j][i] ==- 1   ||  dp[j][i] > dp[j][k] + dp[j][i ^ k])
 62                          dp[j][i] = dp[j][k] + dp[j][i ^ k];
 63              }
 64          }
 65          sz = 0 ;
 66          CLR(vis);
 67          REP(j,N + M + 1 if (dp[j][i] !=- 1 ){
 68              hp[sz ++ ]. set (j,dp[j][i]);
 69              push_heap(hp, hp + sz, cmp);
 70          }
 71           while (sz){
 72               int  now = hp[ 0 ].v;
 73              pop_heap(hp, hp + sz -- ,cmp);
 74               if (vis[now]) continue ;
 75              vis[now] = true ;
 76               for ( int  e = gg[now];  ~ e; e = edg[e].e){
 77                   int  to = edg[e].v, td = dp[now][i] + edg[e].d;
 78                   if (dp[to][i] ==- 1   ||  dp[to][i] > td){
 79                      dp[to][i] = td;
 80                      hp[sz ++ ]. set (to,td);
 81                      push_heap(hp, hp + sz, cmp);
 82                  }
 83              }
 84          }
 85      }
 86       return  dp[ 0 ][up - 1 ];
 87  }
 88 
 89  int  main()
 90  {
 91       while ( ~ scanf( " %d %d %d " & N,  & M,  & P)){
 92           int  u, v, d;
 93          SET(gg, - 1 ); ecnt = 0 ;
 94          REP(i,N + M){
 95              scanf( " %d " & d);
 96              addedge( 0 ,i + 1 , d);
 97          }
 98          REP(i,P){
 99              scanf( " %d %d %d " & u,  & v,  & d);
100              addedge(u,v,d);
101          }
102           int  ret = steiner();
103          printf( " %d\n " , ret);
104      }
105       return   0 ;
106  }

你可能感兴趣的:(hdu 3311 steiner tree / 状态压缩spfa)