分析:
图中点集连通的最小代价,考虑斯坦纳树
这是一道二维平面上的斯坦纳树
xue微更改一下状态
f[i][j][k]
f
[
i
]
[
j
]
[
k
]
,表示根结点是
(i,j)
(
i
,
j
)
,连通状态是
k
k
的最小状态
-
- f[i][j][k]=min{f[x][y][k]+a[i][j]|(i,j)和(x,y)相邻} f [ i ] [ j ] [ k ] = m i n { f [ x ] [ y ] [ k ] + a [ i ] [ j ] | ( i , j ) 和 ( x , y ) 相 邻 }
由于要输出一组合法解,所以我们在转移的时候记录一下转移点即可
最后在构造可行解的时候,我们找到一棵以景点以根的树即可tip
我有一种恍然大明白的感觉,我们用斯坦纳树转移出来的一定是一棵树
如果求的是一片森林,在斯坦纳完事后还要再状压dp一遍
不过这道题求的就是一棵树,所以一遍斯坦纳即可bzoj上需要SPJ的题评测基本都炸了,dada们随意吧
#include<cstdio> #include<cstring> #include<iostream> #include<queue> using namespace std; const int INF=0x33333333; const int N=12; const int zz[4][2]={{0,1},{1,0},{-1,0},{0,-1}}; int n,m,a[N][N],f[N][N][1100],tot=0,mp[N][N]; struct node{ int x,y,s; node(int xx=0,int yy=0,int ss=0) {x=xx;y=yy;s=ss;} }; node g[N][N][1100]; struct point{ int x,y; point(int xx=0,int yy=0) {x=xx;y=yy;} }; queue<point> q; bool in[N][N]; void spfa(int S) { while (!q.empty()) { point now=q.front(); q.pop(); int i=now.x,j=now.y; in[i][j]=0; for (int d=0;d<4;d++) { int x=i+zz[d][0],y=j+zz[d][1]; if (x<1||y<1||x>n||y>m) continue; if (f[x][y][S]>f[i][j][S]+a[x][y]) { f[x][y][S]=f[i][j][S]+a[x][y]; g[x][y][S]=node(i,j,S); if (!in[x][y]) q.push(point(x,y)),in[x][y]=1; } } } } void dp() { for (int S=1;S<(1<<tot);S++) { for (int s=(S-1)&S;s;s=(s-1)&S) for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { if (f[i][j][S]>f[i][j][s]+f[i][j][S^s]-a[i][j]) { f[i][j][S]=f[i][j][s]+f[i][j][S^s]-a[i][j]; g[i][j][S]=node(i,j,s); } } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (f[i][j][S]<INF&&!in[i][j]) q.push(point(i,j)),in[i][j]=1; spfa(S); } } void get(int i,int j,int s) { if (i<1||j<1||i>n||j>m) return; mp[i][j]=1; node t=g[i][j][s]; if (t.s==s) get(t.x,t.y,t.s); else get(t.x,t.y,t.s),get(t.x,t.y,s^t.s); } void solve() { int x,y; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (!a[i][j]) {x=i,y=j;break;} printf("%d\n",f[x][y][(1<<tot)-1]); get(x,y,(1<<tot)-1); for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) { if (!a[i][j]) printf("x"); else if (mp[i][j]) printf("o"); else printf("-"); } printf("\n"); } } int main() { memset(f,0x33,sizeof(f)); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { scanf("%d",&a[i][j]); if (!a[i][j]) f[i][j][1<<tot]=0,tot++; } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) for (int k=1;k<(1<<tot);k++) g[i][j][k]=node(-1,-1,-1); dp(); solve(); return 0; }