• bzoj2595 [Wc2008]游览计划


    题目描述

    题解:

    这道题是斯坦纳树的典型例题。

    首先看一眼数据范围发现$10$的范围不状压对不起出题人,然后考虑转移。

    设$f[i][j][s]$表示当前在点$(i,j)$,覆盖特殊节点状态为$s$的最小花费。

    转移有:

    1.$s$不变,此时有$f[i][j][s]=min(f[i'][j'][s]+a[i'][j'])$;

    2.$(i,j)$不变,此时有$f[i][j][s]=min(f[i][j][t]+f[i][j][s$^$t])$,其中$t$是$s$的子集。

    转移1是最短路形式,我们可用spfa转移;

    转移2直接枚举子集。

    代码:

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 15
    int n,m;
    int f[N][N][1<<10],mp[N][N];
    int cnt;
    struct Pair
    {
        int x,y;
        Pair(){}
        Pair(int x,int y):x(x),y(y){}
    }p[N];
    struct Three
    {
        int x,y,s;
        Three(){}
        Three(int x,int y,int s):x(x),y(y),s(s){}
    }fa[N][N][1<<10];
    int dx[4]={-1,1,0,0};
    int dy[4]={0,0,-1,1};
    bool vis[N][N];
    queue<Pair>q;
    bool check(int x,int y)
    {
        return x>=1&&x<=n&&y>=1&&y<=m;
    }
    void spfa(int s)
    {
        while(!q.empty())
        {
            Pair tp = q.front();
            q.pop();
            int x = tp.x,y = tp.y,tx,ty;
            for(int i=0;i<4;i++)
            {
                tx = x+dx[i],ty = y+dy[i];
                if(!check(tx,ty))continue;
                if(f[x][y][s]+mp[x][y]<f[tx][ty][s])
                {
                    f[tx][ty][s]=f[x][y][s]+mp[x][y];
                    fa[tx][ty][s] = Three(x,y,s);
                    if(!vis[tx][ty])
                    {
                        q.push(Pair(tx,ty));
                        vis[tx][ty] = 1;
                    }
                }
            }
            vis[x][y] = 0;
        }
    }
    bool ot[N][N];
    void dfs(int x,int y,int s)
    {
        if(!check(x,y))return ;
        ot[x][y] = 1;
        Three tmp = fa[x][y][s];
        if(tmp.s==s)
        {
            dfs(tmp.x,tmp.y,s);
        }else
        {
            dfs(tmp.x,tmp.y,tmp.s);
            dfs(tmp.x,tmp.y,s^tmp.s);
        }
    }
    int main()
    {
    //        freopen("7.in","r",stdin);
        scanf("%d%d",&n,&m);
        memset(f,0x3f,sizeof(f));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&mp[i][j]);
                if(!mp[i][j])
                {
                    p[cnt] = Pair(i,j);
                    f[i][j][1<<cnt] = 0;
                    cnt++;
                }
            }
        }
        for(int s = 1;s<(1<<cnt);spfa(s),s++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {
                    for(int t=s&(s-1);t;t=(t-1)&s)
                        if(f[i][j][s]>f[i][j][s^t]+f[i][j][t])
                        {
                            f[i][j][s]=f[i][j][s^t]+f[i][j][t];
                            fa[i][j][s] = Three(i,j,t);
                        }
                    if(f[i][j][s]<0x3f3f3f3f)q.push(Pair(i,j)),vis[i][j]=1;
                }
        printf("%d
    ",f[p[0].x][p[0].y][(1<<cnt)-1]);
        dfs(p[0].x,p[0].y,(1<<cnt)-1);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(!mp[i][j])putchar('x');
                else if(ot[i][j])putchar('o');
                else putchar('_');
            }
            puts("");
        }
        return 0;
    }
  • 相关阅读:
    软件概要设计说明书(初稿) 定稿
    重新确定了数据流图以及模块图2020.5.4
    开始编写概要说明书以及详细说明书2020.4.29
    singleflight是如何避免缓存击穿的?
    从IO 到BIO/NIO/AIO 浅析
    JVM
    Http
    Linux命令
    什么时候触发MinorGC?什么时候触发FullGC?
    计算机网络
  • 原文地址:https://www.cnblogs.com/LiGuanlin1124/p/10206806.html
Copyright © 2020-2023  润新知