题解:斯坦纳树,实现神马的在代码里面有还看得过去的注释。
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 15
#define inf 0x3f3f3f3f
using namespace std;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
struct Point
{
int x,y;
Point(int _x=0,int _y=0):x(_x),y(_y){}
};
struct Path
{
int x,y,s;
Path(int _x=0,int _y=0,int _s=0):x(_x),y(_y),s(_s){}
}path[N][N][1<<N];
queue<Point>q;
int n,m,cnt;
int map[N][N],id[N];
int f[N][N][1<<N]; // 别被三维迷惑了,这里的前两维存了坐标
// 第三维才是存的已连接点的压缩状态
bool inq[N][N],used[N][N];
void spfa(int S)
{
int k,x,y,X,Y;
while(!q.empty()) // 一旦某状态被更新了值,
// 就可以去再更新一遍别的状态。
{
Point U=q.front();q.pop();
inq[x=U.x][y=U.y]=false;
for(k=0;k<4;k++) // 向上下左右进行更新
{
X=x+dx[k],Y=y+dy[k];
if(x<1||x>n||y<1||y>m)continue; // 不能出界、
if(f[X][Y][S]>f[x][y][S]+map[X][Y]) // 更新值
{
f[X][Y][S]=f[x][y][S]+map[X][Y];
path[X][Y][S]=Path(x,y,S); // 记录由什么转移
if(!inq[X][Y])q.push(Point(X,Y)),inq[X][Y]=true;
}
}
}
}
void dfs(int x,int y,int S) // 乱搞求解
{
if(x>inf||!path[x][y][S].s)return ;
used[x][y]=1;
dfs(path[x][y][S].x,path[x][y][S].y,path[x][y][S].s);
if(path[x][y][S].x==x&&path[x][y][S].y==y)dfs(x,y,S^path[x][y][S].s);
}
int main()
{
// freopen("test.in","r",stdin);
int i,j,k;
// 读入
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)for(j=1;j<=m;j++)
scanf("%d",&map[i][j]),cnt+=(!map[i][j]);
for(i=id[0]=1;i<=cnt;i++)id[i]=id[i-1]<<1;
// 初始化一些值
for(i=1;i<=n;i++)for(j=1;j<=m;j++)for(k=0;k<id[cnt];k++) // 初始化
f[i][j][k]=path[i][j][k].x=inf;
for(i=1,cnt=0;i<=n;i++)for(j=1;j<=m;j++)if(!map[i][j]) // 景点的f赋0
f[i][j][id[cnt++]]=0; // 注意此处保证了顺序
// 状压求斯坦纳树
int S,s,tmp;
for(S=1;S<id[cnt];spfa(S++)) // 枚举点的连通性
for(i=1;i<=n;i++) // 枚举点
for(j=1;j<=m;j++)
{
for(s=S&(S-1);s;s=S&(s-1))// 枚举集合的子集 Orz Orz 这里不妨调一下看看变量就好了
if((tmp=f[i][j][s]+f[i][j][S^s]-map[i][j])<f[i][j][S]) // 更新f值
{
f[i][j][S]=tmp;
path[i][j][S]=Path(i,j,s);
}
if(f[i][j][S]<inf) // 这个状态被更新过,就可以去更新别人了
q.push(Point(i,j)),inq[i][j]=1;
}
// 求解、 这之后都不是斯坦纳树普遍模板了,乱搞就好
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)if(!map[i][j]) //经过dfs后权值被缩到了一起~
{
dfs(i,j,id[cnt]-1);
printf("%d\n",f[i][j][id[cnt]-1]);
break ;
}
if(j<=m)break;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
if(!map[i][j])putchar('x');
else if(used[i][j])putchar('o');
else putchar('_');
}
puts("");
}
return 0;
}