题解:
这道题是斯坦纳树的典型例题。
首先看一眼数据范围发现$10$的范围不状压对不起出题人,然后考虑转移。
设$f[i][j][s]$表示当前在点$(i,j)$,覆盖特殊节点状态为$s$的最小花费。
转移有:
1.$s$不变,此时有$f[i][j][s]=min(f[i'][j'][s]+a[i'][j'])$;
2.$(i,j)$不变,此时有$f[i][j][s]=min(f[i][j][t]+f[i][j][s$^$t])$,其中$t$是$s$的子集。
转移1是最短路形式,我们可用spfa转移;
转移2直接枚举子集。
代码:
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 15 int n,m; int f[N][N][1<<10],mp[N][N]; int cnt; struct Pair { int x,y; Pair(){} Pair(int x,int y):x(x),y(y){} }p[N]; struct Three { int x,y,s; Three(){} Three(int x,int y,int s):x(x),y(y),s(s){} }fa[N][N][1<<10]; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; bool vis[N][N]; queue<Pair>q; bool check(int x,int y) { return x>=1&&x<=n&&y>=1&&y<=m; } void spfa(int s) { while(!q.empty()) { Pair tp = q.front(); q.pop(); int x = tp.x,y = tp.y,tx,ty; for(int i=0;i<4;i++) { tx = x+dx[i],ty = y+dy[i]; if(!check(tx,ty))continue; if(f[x][y][s]+mp[x][y]<f[tx][ty][s]) { f[tx][ty][s]=f[x][y][s]+mp[x][y]; fa[tx][ty][s] = Three(x,y,s); if(!vis[tx][ty]) { q.push(Pair(tx,ty)); vis[tx][ty] = 1; } } } vis[x][y] = 0; } } bool ot[N][N]; void dfs(int x,int y,int s) { if(!check(x,y))return ; ot[x][y] = 1; Three tmp = fa[x][y][s]; if(tmp.s==s) { dfs(tmp.x,tmp.y,s); }else { dfs(tmp.x,tmp.y,tmp.s); dfs(tmp.x,tmp.y,s^tmp.s); } } int main() { // freopen("7.in","r",stdin); scanf("%d%d",&n,&m); memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%d",&mp[i][j]); if(!mp[i][j]) { p[cnt] = Pair(i,j); f[i][j][1<<cnt] = 0; cnt++; } } } for(int s = 1;s<(1<<cnt);spfa(s),s++) for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { for(int t=s&(s-1);t;t=(t-1)&s) if(f[i][j][s]>f[i][j][s^t]+f[i][j][t]) { f[i][j][s]=f[i][j][s^t]+f[i][j][t]; fa[i][j][s] = Three(i,j,t); } if(f[i][j][s]<0x3f3f3f3f)q.push(Pair(i,j)),vis[i][j]=1; } printf("%d ",f[p[0].x][p[0].y][(1<<cnt)-1]); dfs(p[0].x,p[0].y,(1<<cnt)-1); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(!mp[i][j])putchar('x'); else if(ot[i][j])putchar('o'); else putchar('_'); } puts(""); } return 0; }