洛谷 P4294 [WC2008]游览计划 斯坦纳树

题目描述

从未来过绍兴的小D有幸参加了Winter Camp 2008,他被这座历史名城的秀丽风景所吸引,强烈要求游览绍兴及其周边的所有景点。

主办者将绍兴划分为N行M列(N×M)个分块,如下图(8×8):

这里写图片描述

景点含于方块内,且一个方块至多有一个景点。无景点的方块视为路。

为了保证安全与便利,主办方依据路况和治安状况,在非景点的一些方块内安排不同数量的志愿者;在景点内聘请导游(导游不是志愿者)。在选择旅游方案时,保证任意两个景点之间,存在一条路径,在这条路径所经过的每一个方块都有志愿者或者该方块为景点。既能满足选手们游览的需要,又能够让志愿者的总数最少。

例如,在上面的例子中,在每个没有景点的方块中填入一个数字,表示控制该方块最少需要的志愿者数目:

这里写图片描述

图中用深色标出的方块区域就是一种可行的志愿者安排方案,一共需要20名志愿者。由图可见,两个相邻的景点是直接(有景点内的路)连通的(如沈园和八字桥)。

现在,希望你能够帮助主办方找到一种最好的安排方案。

输入输出格式

输入格式:
第一行有两个整数,N和M,描述方块的数目。

接下来N行,每行有M个非负整数,如果该整数为0,则该方块为一个景点;

否则表示控制该方块至少需要的志愿者数目。相邻的整数用(若干个)空格隔开,

行首行末也可能有多余的空格。

输出格式:
由N+1行组成。第一行为一个整数,表示你所给出的方案中安排的志愿者总数目。

接下来N行,每行M个字符,描述方案中相应方块的情况:

‘_’(下划线)表示该方块没有安排志愿者;

‘o’(小写英文字母o)表示该方块安排了志愿者;

‘x’(小写英文字母x)表示该方块是一个景点;

注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不一致(任何一行中,多余的空格都不允许出现),都可能导致该测试点不得分。

输入输出样例

输入样例#1:
4 4
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
输出样例#1:
6
xoox
___o
___o
xoox
说明

所有的 10 组数据中 N, M ,以及景点数 K 的范围规定如下

这里写图片描述

输入文件中的所有整数均不小于 0 且不超过 2^16

分析:在看图论是看到的一种神奇算法。其实是求n个点中,使得k个关键点相互联通的生成树(只要求这k个点连通即可)。不能用最小生成树做的原因是,假设只有x,y两个关键点,他们的最短路不一定是在最小生成
树上。如果跑出两两关键点最短路,再搞最小生成树,就会出现重边的情况,也是不可行的。所有用到一种dp的想法。

设f[s][i]为关键点连通状态为s,以i为根的生成树的最小代价。有

第一种转移:
f[s][i]=min(f[sub][i]+f[s-sub][j],f[s][i])
(其中sub为s的一个子集,s-sub为改子集相对与s的补集)
此处表示合并两棵子树,如果是点权(比如本题),就要减去i的点权(根节点算了两次)

第二种:
f[s|g[j]][j]=min(f[s|g[j]][j],f[s][i]+val[j])
(如果j为关键点,我们给关键点编号,第k个关键点的g[j]=2^(k-1),如果i不是关键点,那么g[j]=0)
此处表示把以i为根的子树进行通过边(i,j)延伸,变为以j为根的子树,假如j为关键点,那么状态s就会发生变化。可以用spfa转移。

两种变换相互独立,可以先第一种转移,后第二种转移,也可以反过来。
如果以s划分层,那么第一种相当与从下面的层转移到上面的层,第二种就是同层转移。也就是说,如果
s|g[j]<>s,那么这个状态就到后面的层再同层转移了。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <algorithm>

const int maxn=12;
const int inf=0x3f3f3f3f;
const int dx[4]={0,1,0,-1};
const int dy[4]={1,0,-1,0};

using namespace std;

char ans[maxn][maxn];
int a[maxn][maxn],f[1<<maxn][maxn][maxn],g[maxn][maxn];
bool vis[maxn][maxn];
int n,m,k,c;

struct node{
    int x1,y1,s1;
    int x2,y2,s2;
}next[1<<maxn][maxn][maxn];

struct rec{
    int x,y;
};

queue <rec> q;


void make()
{
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            for (int s=0;s<(1<<maxn);s++) f[s][i][j]=inf;
        }
    }
}


void spfa(int s)
{
    while (!q.empty())
    {
        rec t=q.front();
        q.pop();
        int i=t.x,j=t.y;
        for (int l=0;l<4;l++)
        {
            int x=i+dx[l],y=j+dy[l];
            if ((x>0) && (x<=n) && (y>0) && (y<=m))
            {
                if (f[s][i][j]+a[x][y]<f[s|g[x][y]][x][y])
                {
                    f[s|g[x][y]][x][y]=f[s][i][j]+a[x][y];
                    next[s|g[x][y]][x][y].x1=i;
                    next[s|g[x][y]][x][y].y1=j;
                    next[s|g[x][y]][x][y].s1=s;
                    next[s|g[x][y]][x][y].x2=0;
                    next[s|g[x][y]][x][y].y2=0;
                    next[s|g[x][y]][x][y].s2=0;
                    if ((s|g[x][y]==s) && (!vis[x][y]))
                    {
                        vis[x][y]=1;
                        rec t=(rec){x,y};
                        q.push(t);
                    }
                }
            }
        }
        vis[i][j]=0;
    }
}

void dfs(int s,int x,int y)
{
    if ((!s) && (!x) && (!y)) return;
    if (ans[x][y]!='x') ans[x][y]='o';
    dfs(next[s][x][y].s1,next[s][x][y].x1,next[s][x][y].y1);
    dfs(next[s][x][y].s2,next[s][x][y].x2,next[s][x][y].y2);
}
int main()
{
    scanf("%d%d",&n,&m);
    make();
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            if (!a[i][j])
            {
                ans[i][j]='x';
                f[1<<k][i][j]=0;
                g[i][j]=1<<k;
                k++;
            }
            else
            {
                f[0][i][j]=a[i][j];
                ans[i][j]='_';
            }
        }
    }               
    for (int s=1;s<(1<<k);s++)
    {
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=m;j++)
            {
                for (int sub=s;sub;sub=(sub-1)&s)
                {
                    if (f[sub][i][j]+f[s-sub][i][j]-a[i][j]<f[s][i][j])
                    {
                        f[s][i][j]=f[sub][i][j]+f[s-sub][i][j]-a[i][j];
                        next[s][i][j].x1=i;
                        next[s][i][j].y1=j;
                        next[s][i][j].s1=sub;
                        next[s][i][j].x2=i;
                        next[s][i][j].y2=j;
                        next[s][i][j].s2=s-sub;
                    }
                }
                vis[i][j]=0;
                if (f[s][i][j]!=inf)
                {
                    rec t=(rec){i,j};
                    q.push(t);
                    vis[i][j]=1;
                }
            }
        }     
        spfa(s);
    }       
    c=inf;
    int s,x,y;
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)
     {
         if (f[(1<<k)-1][i][j]<c)
         {
            c=f[(1<<k)-1][i][j];
            s=(1<<k)-1;
            x=i;
            y=j;
         }   
     }
    printf("%d\n",c);     
    dfs(s,x,y);
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++) printf("%c",ans[i][j]);
        printf("\n");
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/79884803