题目描述
题目大意:给出一个n*m的网格,有一些位置是0,每一个位置有一个权值,求一种最小的权值覆盖,使任意两个0的位置都连通。输出方案。
题解
f(i,j,s)表示和第(i,j)个方格连通的关键方格状态为s的最小价值
那么有两种转移:
f(i,j,s)=min{f(i,j,s-t)+f(i,j,t)-a(i,j)}
f(i,j,s)=min{f(k,l,s)+a(i,j)}
第一种转移实际上是枚举s的一个拆分,将其拆分成s-t和t
第二种转移实际上是枚举另外一个点,并且可以发现转移的形式和最短路的形式非常像,所以转移的过程可以借助spfa
最后记录一下转移的路径输出方案
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define N 10
int n,m,inf,cnt,ans;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int a[N+1][N+1],f[N+1][N+1][1<<N],s[N+1][N+1];bool vis[N+1][N+1];
struct data{int x,y,sta;}pre[N+1][N+1][1<<N];
queue <data> q;
void spfa(int sta)
{
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
if (f[i][j][sta]!=inf) q.push((data){i,j,sta});
memset(vis,0,sizeof(vis));
while (!q.empty())
{
data now=q.front();q.pop();
vis[now.x][now.y]=0;
for (int i=0;i<4;++i)
{
int x=now.x+dx[i],y=now.y+dy[i];
if (x<1||x>n||y<1||y>m) continue;
if (f[x][y][sta]>f[now.x][now.y][sta]+a[x][y])
{
f[x][y][sta]=f[now.x][now.y][sta]+a[x][y];
pre[x][y][sta]=(data){now.x,now.y,sta};
if (!vis[x][y]) vis[x][y]=1,q.push((data){x,y,sta});
}
}
}
}
void print(int x,int y,int sta)
{
s[x][y]=1;
if (pre[x][y][sta].x&&pre[x][y][sta].y)
{
print(pre[x][y][sta].x,pre[x][y][sta].y,pre[x][y][sta].sta);
if (pre[x][y][sta].x==x&&pre[x][y][sta].y==y) print(pre[x][y][sta].x,pre[x][y][sta].y,sta-pre[x][y][sta].sta);
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(f,127/3,sizeof(f));inf=f[0][0][0];
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
scanf("%d",&a[i][j]);
if (!a[i][j])
{
++cnt;
f[i][j][1<<(cnt-1)]=0;
}
}
for (int k=0;k<1<<cnt;++k)
{
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
for (int sta=(k-1)&k;sta;sta=(sta-1)&k)
if (f[i][j][k]>f[i][j][sta]+f[i][j][k-sta]-a[i][j])
{
f[i][j][k]=f[i][j][sta]+f[i][j][k-sta]-a[i][j];
pre[i][j][k]=(data){i,j,sta};
}
}
spfa(k);
}
ans=inf;int x,y;
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
if (ans>f[i][j][(1<<cnt)-1])
{
ans=f[i][j][(1<<cnt)-1];
x=i,y=j;
}
print(x,y,(1<<cnt)-1);
printf("%d\n",ans);
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
if (s[i][j])
{
if (!a[i][j]) putchar('x');
else putchar('o');
}
else putchar('_');
if (j==m) puts("");
}
}