[BZOJ]2595: [Wc2008]游览计划 斯坦纳树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_36797646/article/details/85226622

题解:

因为要做另一道题,所以来学了这个东西。
斯坦纳树解决的是这样一个问题:有若干个点,其中一些是关键点,点之间有边权,求把所有关键点连起来的最小代价。
这道题就是一道裸题。显然连接方式一定是一颗树, f [ i ] [ S ] f[i][S] 表示以 i i 为根,已经连接的关键点的状态为 S S 的最小代价。
然后分两种情况转移:
1、 f [ i ] [ S ] = min { f [ i ] [ s ] + f [ i ] [ S s ] } f[i][S]=\min\{f[i][s]+f[i][S\oplus s]\} s s S S 的子集。
2、 f [ i ] [ S ] = min { f [ j ] [ S ] + a [ i ] } f[i][S]=\min\{f[j][S]+a[i]\} i i j j 相邻。
第一种转移直接枚举子集,第二种转移用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("");
        }
    }
}


猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/85226622