bzoj2595 [Wc2008]游览计划 斯坦纳树
Description
Solution
一开始还以为是什么奇妙的网络流。。
斯坦纳树裸题。所谓斯坦纳树就是求原图的一个子图,使得给定点集连通且边权之和最小。大概是最小生成树的拓展
设f[i,x]表示给定点集的连通性为i,下一条边可以从与x相邻的边中选的答案,我们可以
- 枚举i的子集设为j,f[i,x]=min(f[i,x],f[j,x]+f[i-j,x])
- 枚举x的邻点设为y,f[i,x]=min(f[i,x],f[i,y]+w[x,y])
第一个转移可以直接上,第二个转移可以用最短路松弛。注意这里是一般的模型,权值都在边上
本题就是f[i,j,x]表示i行j列状态为x的答案,转移的时候注意权值在点上就可以了
输出方案的话每次记录转移方式,然后dfs即可
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
const int INF=0x3f3f3f3f;
const int N=12;
int f[N][N][(1<<N)+5];
int a[N][N],wjp[N][N][(1<<N)+5];
int xx[N][N][(1<<N)+5],yy[N][N][(1<<N)+5];
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int id[N][N],n,m;
bool prt[N][N],vis[N][N];
std:: queue <int> que;
void dfs(int x,int y,int now) {
prt[x][y]=true;
if (!f[x][y][now]) return ;
if (wjp[x][y][now]) dfs(xx[x][y][now],yy[x][y][now],now);
else {
dfs(x,y,xx[x][y][now]);
dfs(x,y,yy[x][y][now]);
}
}
void spfa(int rec) {
for (;!que.empty();) {
int x=que.front(); que.pop();
int y=que.front(); que.pop();
rep(k,0,3) {
int tx=x+dx[k],ty=y+dy[k];
if (!tx||!ty||tx>n||ty>m) continue;
if (x&&f[x][y][rec]+a[tx][ty]<f[tx][ty][rec]) {
f[tx][ty][rec]=f[x][y][rec]+a[tx][ty];
wjp[tx][ty][rec]=1;
xx[tx][ty][rec]=x;
yy[tx][ty][rec]=y;
if (!vis[tx][ty]) {
que.push(tx); que.push(ty);
vis[tx][ty]=true;
}
}
} vis[x][y]=false;
}
}
int main(void) {
int cnt=0; scanf("%d%d",&n,&m);
fill(f,31);
rep(i,1,n) rep(j,1,m) {
scanf("%d",&a[i][j]);
if (!a[i][j]) {
cnt++;
f[i][j][1<<(cnt-1)]=0;
}
}
rep(now,1,(1<<cnt)-1) {
fill(vis,0);
rep(i,1,n) rep(j,1,m) {
for (int tmp=(now-1)&now;tmp;tmp=(tmp-1)&now) {
if (f[i][j][tmp]+f[i][j][now-tmp]-a[i][j]<f[i][j][now]) {
wjp[i][j][now]=0;
xx[i][j][now]=tmp;
yy[i][j][now]=now-tmp;
f[i][j][now]=f[i][j][tmp]+f[i][j][now-tmp]-a[i][j];
}
}
if (f[i][j][now]!=f[0][0][0]) que.push(i),que.push(j),vis[i][j]=true;
}
spfa(now);
}
int ans=INF,stx,sty;
rep(i,1,n) rep(j,1,m) if (!a[i][j]) {
if (f[i][j][(1<<cnt)-1]<ans) {
ans=f[i][j][(1<<cnt)-1];
stx=i,sty=j;
}
}
printf("%d\n", ans);
dfs(stx,sty,(1<<cnt)-1);
rep(i,1,n) {
rep(j,1,m) {
if (!a[i][j]) putchar('x');
else if (prt[i][j]) putchar('o');
else putchar('_');
} puts("");
}
return 0;
}