http://www.lydsy.com/JudgeOnline/problem.php?id=2595

题意:

BZOJ 2595: [Wc2008]游览计划(斯坦纳树)_#include

BZOJ 2595: [Wc2008]游览计划(斯坦纳树)_状态转移_02

 

思路:

学习了一下斯坦纳树,这个我老是想到lol中的斯卡纳。。。

斯坦纳树和最小生成树是差不多的,斯坦纳树是将给定点连接起来的最小代价,可以额外增加点。主要是通过状压dp和spfa来做。

f [ i ] [ j ] [ now ] 表示以$(i,j)$为根节点并且状态为now时的最小代价。

它有两个状态转移方程:

f[i][j][now]=min(f[i][j][now],f[i][j][s]+f[i][j][now-s]-a[i][j]) 

这个是枚举子集,将两颗子树合并起来,要注意的一点就是根节点a[i][j]会重复计算依次,这也就是为什么最后还要减去a[i][j]。

至于枚举子集的方法就是:

for(int sub=(state-1)&state;sub;sub=(sub-1)&state)

另一个状态转移方程就是:

f[i][j][now]=min(f[i][j][now],f[i'][j'][now]+a[i][j])

这个就是往上下左右四个方向拓展,因为当一个状态的最优解确定,与它相邻的节点的最优解也就能确定下来。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<sstream>
  6 #include<vector>
  7 #include<stack>
  8 #include<queue>
  9 #include<cmath>
 10 #include<map>
 11 #include<set>
 12 using namespace std;
 13 typedef long long ll;
 14 typedef pair<int,ll> pll;
 15 const int INF = 0x3f3f3f3f;
 16 const int maxn=100+5;
 17 
 18 int n, m, k;
 19 int a[15][15];
 20 int ans[15][15];
 21 int inq[20005];
 22 int f[15][15][2005];
 23 
 24 int dx[5]={1,-1,0,0};
 25 int dy[5]={0,0,1,-1};
 26 
 27 struct node
 28 {
 29     int i,j,state;
 30 }path[15][15][2005];
 31 
 32 struct point
 33 {
 34     int i,j;
 35 };
 36 
 37 queue<point> Q;
 38 
 39 int c(point x)
 40 {
 41     return (x.i-1)*n+x.j;
 42 }
 43 
 44 void spfa(int state)
 45 {
 46     while (!Q.empty())
 47     {
 48         point x=Q.front(); Q.pop(); inq[c(x)]=0;
 49         for (int k=0;k<4;k++)
 50         {
 51             point y;
 52             y.i=x.i+dx[k],y.j=x.j+dy[k];
 53             if (y.i<1||y.j<1||y.i>n||y.j>m) continue;
 54             if (f[y.i][y.j][state]>f[x.i][x.j][state]+a[y.i][y.j])
 55             {
 56                 f[y.i][y.j][state]=f[x.i][x.j][state]+a[y.i][y.j];
 57                 path[y.i][y.j][state].i=x.i,path[y.i][y.j][state].j=x.j,path[y.i][y.j][state].state=state;
 58                 if (!inq[c(y)])  {Q.push(y),inq[c(y)]=1;}
 59             }
 60         }
 61     }
 62 }
 63 
 64 void dfs(int x, int y, int state)
 65 {
 66     if(!path[x][y][state].state)  return;
 67     ans[x][y]=1;
 68     dfs(path[x][y][state].i,path[x][y][state].j,path[x][y][state].state);
 69     if(path[x][y][state].i==x && path[x][y][state].j==y)
 70         dfs(x,y,state^path[x][y][state].state);
 71 }
 72 
 73 int main()
 74 {
 75     //freopen("in.txt","r",stdin);
 76     while(~scanf("%d%d",&n,&m))
 77     {
 78         k=0;
 79         memset(f,0x3f,sizeof(f));
 80         memset(ans,0,sizeof(ans));
 81         memset(path,0,sizeof(path));
 82         for(int i=1;i<=n;i++)
 83         for(int j=1;j<=m;j++)
 84         {
 85             scanf("%d",&a[i][j]);
 86             if(!a[i][j])  f[i][j][1<<(k++)]=0;
 87         }
 88 
 89         for(int state=1;state<(1<<k);state++)
 90         {
 91             for(int i=1;i<=n;i++)
 92             for(int j=1;j<=m;j++)
 93             {
 94                 for(int sub=(state-1)&state;sub;sub=(sub-1)&state)   //枚举子集,将两个子树合并
 95                 {
 96                     if(f[i][j][state]>f[i][j][sub]+f[i][j][state-sub]-a[i][j])  //将两颗树合并,a[i][j]这个根顶点会多算一次,减掉
 97                     {
 98                         f[i][j][state]=f[i][j][sub]+f[i][j][state-sub]-a[i][j];
 99                         path[i][j][state].i=i,path[i][j][state].j=j,path[i][j][state].state=sub; //路径记录
100                     }
101                 }
102                 if(f[i][j][state]!=INF)
103                 {
104                     point p;
105                     p.i=i, p.j=j;
106                     Q.push(p); inq[c(p)]=1;
107                 }
108             }
109             spfa(state);
110         }
111 
112         int x,y;
113         for(int i=1;i<=n;i++)
114         for(int j=1;j<=m;j++)
115             if(!a[i][j])  {x=i;y=j;break;}
116 
117         printf("%d\n",f[x][y][(1<<k)-1]);
118 
119         dfs(x,y,(1<<k)-1);
120         for(int i=1;i<=n;i++)
121         {
122             for(int j=1;j<=m;j++)
123             {
124                 if(!a[i][j])  printf("x");
125                 else if(ans[i][j])  printf("o");
126                 else printf("_");
127             }
128             printf("\n");
129         }
130     }
131     return 0;
132 }