版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_36797646/article/details/85226622
题解:
因为要做另一道题,所以来学了这个东西。
斯坦纳树解决的是这样一个问题:有若干个点,其中一些是关键点,点之间有边权,求把所有关键点连起来的最小代价。
这道题就是一道裸题。显然连接方式一定是一颗树,
表示以
为根,已经连接的关键点的状态为
的最小代价。
然后分两种情况转移:
1、
,
为
的子集。
2、
,
与
相邻。
第一种转移直接枚举子集,第二种转移用spfa来转移,两种转移顺序不能颠倒。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=110;
const int inf=700000000;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int tx[]={0,0,1,-1},ty[]={1,-1,0,0};
int n,m,a[Maxn],f[Maxn][1<<10],cnt=0,from[Maxn][1<<10];
bool ch[Maxn];
bool pp(int x,int y){return(x>0&&y>0&&x<=n&&y<=m);}
int P(int x,int y){return(x-1)*m+y;}
struct Edge{int y,next;}e[Maxn<<2];
int last[Maxn],len=0;
void ins(int x,int y)
{
int t=++len;
e[t].y=y;e[t].next=last[x];last[x]=t;
}
queue<int>q;
bool in[Maxn];
void SPFA(int S)
{
while(!q.empty())
{
int x=q.front();q.pop();in[x]=false;
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(f[x][S]+a[y]<f[y][S])
{
f[y][S]=f[x][S]+a[y];from[y][S]=-x;
if(!in[y])in[y]=true,q.push(y);
}
}
}
}
int one(int x){int re=0;for(;x;x-=(x&-x))re++;return re;}
void dfs(int x,int S)
{
if(!f[x][S])return;
ch[x]=true;
if(from[x][S]<0)dfs(-from[x][S],S);
else dfs(x,from[x][S]),dfs(x,S^from[x][S]);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[P(i,j)]=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<4;k++)
{
int x=i+tx[k],y=j+ty[k];
if(pp(x,y))ins(P(i,j),P(x,y));
}
memset(f,63,sizeof(f));
for(int i=1;i<=n*m;i++)if(!a[i])f[i][1<<cnt]=0,cnt++;
for(int S=1;S<(1<<cnt);S++)
{
for(int i=1;i<=n*m;i++)
for(int s=S-1;s;s=(s-1)&S)
if(f[i][s]+f[i][S^s]-a[i]<f[i][S])
f[i][S]=f[i][s]+f[i][S^s]-a[i],from[i][S]=s;
memset(in,false,sizeof(in));
for(int i=1;i<=n*m;i++)
if(f[i][S]<inf)q.push(i),in[i]=true;
SPFA(S);
}
int w=1;
for(int i=2;i<=n*m;i++)if(f[i][(1<<cnt)-1]<f[w][(1<<cnt)-1])w=i;
printf("%d\n",f[w][(1<<cnt)-1]);
dfs(w,(1<<cnt)-1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int t=P(i,j);
if(!a[t])printf("x");
else if(ch[t])printf("o");
else printf("_");
if(j==m)puts("");
}
}
}