BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*

BZOJ2595 Wc2008 游览计划


Description

BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*_第1张图片
BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*_第2张图片

Input

第一行有两个整数,N和 M,描述方块的数目。
接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;
否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开,
行首行末也可能有多余的空格。

Output

由 N + 1行组成。第一行为一个整数,表示你所给出的方案中安排的志愿者总数目。
接下来 N行,每行M 个字符,描述方案中相应方块的情况:
z ‘_’(下划线)表示该方块没有安排志愿者;
z ‘o’(小写英文字母o)表示该方块安排了志愿者;
z ‘x’(小写英文字母x)表示该方块是一个景点;
注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不
一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。

Sample Input

4 4
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0

Sample Output

6
xoox
___o
___o
xoox

HINT

对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内


斯坦纳树,状压DP记录路径
还有一点小小的不同,就是这道题用子集信息进行更新的时候我们需要减去当前点的权值一次,因为两个子集都包含了这个点,权值多算了一次

有关斯坦纳树的总结,请看
算法–斯坦纳树


#include
using namespace std;
#define INF 0x3f3f3f3f
#define N 11
struct Node{int x,y,s;};
int mx[]={0,0,1,-1};
int my[]={1,-1,0,0};
int n,m,maxs;
int cost[N][N],st[N][N],vis[N][N],cnt;
int d[N][N][1<1<bool in[N][N][1<queue Q;
void SPFA(){
    while(!Q.empty()){
        Node t=Q.front();Q.pop();
        int x=t.x,y=t.y,s=t.s;
        in[x][y][s]=0;
        for(int i=0;i<4;i++){
            int tx=x+mx[i],ty=y+my[i],ts=s|st[tx][ty];
            if(tx<1||tx>n||ty<1||ty>m)continue;
            if(d[tx][ty][ts]>d[x][y][s]+cost[tx][ty]){
                d[tx][ty][ts]=d[x][y][s]+cost[tx][ty];
                pre[tx][ty][ts]=t;
                if(!in[tx][ty][ts]&&s==ts){
                    in[tx][ty][ts]=1;
                    Q.push((Node){tx,ty,ts});
                }
            }
        }

    }
}
void dfs(int x,int y,int s){
    vis[x][y]=1;
    Node t=pre[x][y][s];
    int tx=t.x,ty=t.y,ts=t.s;
    if(!tx&&!ty&&!ts)return;
    dfs(tx,ty,ts);
    if(x==tx&&y==ty)dfs(x,y,(s-ts)|st[x][y]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&cost[i][j]);
            if(!cost[i][j])st[i][j]=1<<(cnt++);
        }
    maxs=(1<1;
    memset(d,0x3f,sizeof(d));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(st[i][j])d[i][j][st[i][j]]=0;
    for(int k=0;k<=maxs;k++){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                if(st[i][j]&&!(st[i][j]&k))continue;
                for(int x=(k-1)&k;x;x=(x-1)&k){
                    int t=d[i][j][x|st[i][j]]+d[i][j][(k-x)|st[i][j]]-cost[i][j];
                    if(tif(d[i][j][k]1;
                }
            }
        SPFA();
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)if(st[i][j]){
            printf("%d\n",d[i][j][maxs]);
            dfs(i,j,maxs);
            for(int x=1;x<=n;x++){
                for(int y=1;y<=m;y++){
                    if(st[x][y])putchar('x');
                    else if(vis[x][y])putchar('o');
                    else putchar('_');
                }
                putchar('\n');
            }
            return 0;
        }
    return 0;
}

你可能感兴趣的:(斯坦纳树,状压DP,c++,BZOJ,DP,斯坦纳树,DP,算法,好题)