[BZOJ]2595: [Wc2008]遊覽計劃 斯坦納樹
阿新 • • 發佈:2018-12-24
題解:
因為要做另一道題,所以來學了這個東西。
斯坦納樹解決的是這樣一個問題:有若干個點,其中一些是關鍵點,點之間有邊權,求把所有關鍵點連起來的最小代價。
這道題就是一道裸題。顯然連線方式一定是一顆樹,
表示以
為根,已經連線的關鍵點的狀態為
的最小代價。
然後分兩種情況轉移:
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("");
}
}
}