斯坦纳树板子题。
考虑状压dp,设f[i][j][S]表示当前在点(i,j)考虑转移,其所在的联通块包含的关键点集(至少)为S的答案。
转移时首先枚举子集,有f[i][j][S]=min{f[i][j][x]+f[i][j][y]-a[i][j]} (x&y=0,x|y=S)。
然后考虑从点(i,j)从哪拓展而来,有f[i][j][S]=min{f[x][y][S]}+a[i][j],其中(x,y)为(i,j)的相邻点,使用spfa转移。
这里第二种转移仅在相同关键点集下进行,因为由更小点集转移而来的情况已在第一种转移中被考虑。
对于输出方案,记录如何转移而来即可。
其实并没有搞懂。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 12 int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,s,a[N][N],id[N][N],f[N][N][1<<N],from[N][N][1<<N][2],q[N*N*N*N][2]; int wx[4]={1,0,0,-1},wy[4]={0,1,-1,0}; bool flag[N][N]; char b[N][N]; void inc(int &x){x++;if (x>n*m+1) x-=n*m+1;} void spfa(int k) { int head=0,tail=0; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) tail++,q[tail][0]=i,q[tail][1]=j,flag[i][j]=1; do { inc(head);int x=q[head][0],y=q[head][1];flag[x][y]=0; for (int i=0;i<4;i++) { int u=x+wx[i],v=y+wy[i]; if (u&&u<=n&&v&&v<=m&&f[x][y][k]+a[u][v]<f[u][v][k]) { f[u][v][k]=f[x][y][k]+a[u][v]; from[u][v][k][0]=x,from[u][v][k][1]=y; if (!flag[u][v]) flag[u][v]=1,inc(tail),q[tail][0]=u,q[tail][1]=v; } } }while (head!=tail); } void getans(int x,int y,int k) { b[x][y]='o'; while (from[x][y][k][0]>0) { int u=from[x][y][k][0],v=from[x][y][k][1]; b[x=u][y=v]='o'; } if (from[x][y][k][0]<0) getans(x,y,-from[x][y][k][0]),getans(x,y,-from[x][y][k][1]); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2595.in","r",stdin); freopen("bzoj2595.out","w",stdout); #endif n=read(),m=read(); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { a[i][j]=read(),s+=(a[i][j]==0); if (!a[i][j]) id[i][j]=s; } memset(f,42,sizeof(f)); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { f[i][j][0]=a[i][j]; if (id[i][j]) f[i][j][1<<id[i][j]-1]=a[i][j]; } for (int k=1;k<(1<<s);k++) { for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) for (int x=k;x>=(k^x);x=x-1&k) if (f[i][j][x]+f[i][j][k^x]-a[i][j]<f[i][j][k]) { f[i][j][k]=f[i][j][x]+f[i][j][k^x]-a[i][j]; from[i][j][k][0]=-x,from[i][j][k][1]=-(k^x); } spfa(k); } int ans=100000000; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) ans=min(ans,f[i][j][(1<<s)-1]); cout<<ans<<endl; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) b[i][j]='_'; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (f[i][j][(1<<s)-1]==ans) { getans(i,j,(1<<s)-1); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (!a[i][j]) b[i][j]='x'; for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) putchar(b[i][j]); cout<<endl; } return 0; } }