bzoj2595 [Wc2008]游览计划

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2595

【题解】

斯坦纳树模板题。学了一发斯坦纳树。

对于一般的斯坦纳树,是 给出一些点和一些关键点和边,要求选择权值和最小的连通块使得关键点连通。

那么一般我们用f(x,status)表示在x,状态为status的最小权值和。

本题我们采用f(i,j,status)表示在(i,j),状态为status的最小权值和。

一开始权值就是题目给的,如果是景点那么在对应的标号的status赋值即可。

首先可以分成两个部分来转移:f(i,j,status) = f(i, j, subset) + f(i, j, status - subset) - mp[i][j]

(也就是枚举子集,因为(i,j)被多算了一次,所以要减掉)

另外一面,f(i, j, status) = min(f(i‘, j‘, s‘) + mp[i][j])

(从周围四个格子转移过来,增加一个格子,如果是景点需要特别处理下s‘)

这个式子长得非常像最短路,所以我们可以用spfa来解决。

然后就处理完了。接着说输出答案。

我们记录从哪里转移得到即可。递归下去处理答案并输出。

好麻烦啊qwq第一次题解写这么长呀。

不过斯坦纳树应用好像不大多,了解就行。

# include <queue>
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 10 + 10, STATUS = 1027, N = 500010;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, m, k = 0;
int mp[M][M], st[M][M];
int f[M][M][STATUS];
const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1}; 

struct pa {
    int i, j, s;
    pa() {}
    pa(int i, int j, int s) : i(i), j(j), s(s) {}
    inline int set() {
        return i+11*j+11*11*s;
    }
    friend bool operator == (pa a, pa b) {
        return a.i == b.i && a.j == b.j && a.s == b.s;
    }
};

pa from[M][M][STATUS];
queue<pa> q;
bool vis[N];
bool ans[M][M];

inline void getans(int x, int y, int s) {
    ans[x][y] = 1;
    pa t = from[x][y][s];
    if(t == pa(-1, -1, -1)) return ;
    getans(t.i, t.j, t.s);
    if(t.i==x && t.j==y) getans(t.i, t.j, s-t.s);
}

int main() {
    for (int i=1; i<=10; ++i)
        for (int j=1; j<=10; ++j)
            for (int k=0; k<=1025; ++k) f[i][j][k] = 1e9, from[i][j][k] = pa(-1, -1, -1);
    scanf("%d%d", &n, &m);
    for (int i=1; i<=n; ++i)
        for (int j=1; j<=m; ++j) {
            f[i][j][0] = mp[i][j];
            scanf("%d", &mp[i][j]);
            if(mp[i][j] == 0)
                f[i][j][1<<k] = 0, st[i][j] = (1<<k), ++k;
        }
    int status_size = (1<<k)-1;
    for (int status=1; status<=status_size; ++status) {
        for (int i=1; i<=n; ++i)
            for (int j=1; j<=m; ++j) {
                for (int ss = status-1&status; ss; ss=ss-1&status) {
                    if(f[i][j][status] > f[i][j][ss] + f[i][j][status^ss] - mp[i][j]) {
                        f[i][j][status] = f[i][j][ss] + f[i][j][status^ss] - mp[i][j];
                        from[i][j][status] = pa(i, j, ss);
                    }
                }
                if(f[i][j][status] != 1e9) {
                    pa t = pa(i, j, status);
                    q.push(t);
                    vis[t.set()] = 1;
                }
            }
        while(!q.empty()) {
            pa top = q.front(); q.pop(); vis[top.set()] = 0;
            for (int i=0; i<4; ++i) {
                int xx = top.i+dx[i], yy = top.j+dy[i];
                if(xx<1 || yy<1 || xx>n || yy>m) continue;
                int ss = (top.s | st[xx][yy]);
                if(f[xx][yy][ss] > f[top.i][top.j][top.s] + mp[xx][yy]) {
                    f[xx][yy][ss] = f[top.i][top.j][top.s] + mp[xx][yy];
                    from[xx][yy][ss] = top;
                    pa ns = pa(xx, yy, ss);
                    if(!vis[ns.set()]) {
                        vis[ns.set()] = 1;
                        q.push(ns);
                    }
                }
            }
        }
    }

    for (int i=1; i<=n; ++i)
        for (int j=1; j<=m; ++j) {
            if(!mp[i][j]) {
                printf("%d\n", f[i][j][status_size]);
                getans(i, j, status_size);
                for (int x=1; x<=n; ++x, puts(""))
                    for (int y=1; y<=m; ++y)
                        if(ans[x][y]) {
                            if(mp[x][y]) printf("o");
                            else printf("x");
                        } else printf("_");
                i = n+1, j = m+1;
            }
        }
    return 0;
}

时间: 2024-10-14 02:31:37

bzoj2595 [Wc2008]游览计划的相关文章

BZOJ2595: [Wc2008]游览计划(斯坦纳树,状压DP)

Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 2030  Solved: 986[Submit][Status][Discuss] Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Outpu

【BZOJ2595】[Wc2008]游览计划 斯坦纳树

[BZOJ2595][Wc2008]游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N + 1行组成.第一行为一个整数,表示你所给出的方案中安排的志愿者总数目. 接下来 N行,每行M 个字符,描述方案中相应方块的情况: z  ‘_’(下划线)表示该

[Wc2008]游览计划

2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special Judge[Submit][Status][Discuss] Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N

【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)

2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 1572  Solved: 739 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N

BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]【学习笔记】

传送门 题意:略 论文 <SPFA算法的优化及应用> http://www.cnblogs.com/lazycal/p/bzoj-2595.html 本题的核心就是求斯坦纳树: Steiner Tree: Given an undirected graph with non-negative edge weights and a subset of vertices, usually referred to as terminals, the Steiner tree problem in g

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

[题目分析] 斯坦纳树=子集DP+SPFA? 用来学习斯坦纳树的模板. 大概就是用二进制来表示树包含的点,然后用跟几点表示树的形态. 更新分为两种,一种是合并两个子集,一种是换根,换根用SPFA迭代即可. [代码] #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <set> #include <map> #include

BZOJ 2595 Wc2008 游览计划 斯坦纳树

题目大意:给定一个矩阵,有一些关键点,每个格子有权值,选择一些格子使所有关键点连通,求最小权值和 传说中的斯坦纳树- - 感觉不是很难理解的样子 枚举连通的状态,对于每个状态先对每个位置枚举子集进行合并,然后对这个状态的分层图进行SPFA 看了几分代码还是ZKY写的比较简洁- - 此外就是终于能通过操作符重载访问结构体里的三维数组了- - 我真是太丧病了233 #include <cstdio> #include <cstring> #include <iostream>

BZOJ_2595_[Wc2008]游览计划_斯坦纳树

题意: 分析: 斯坦纳树裸题,有几个需要注意的地方 给出矩阵,不用自己建图,但枚举子集转移时会算两遍,需要减去当前点的权值 方案记录比较麻烦,两边的转移都需要记录,最后dfs找方案会比较容易理解 代码: #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; #define N 110 #define LL long l

[WC2008]游览计划 「斯坦那树模板」

斯坦那树 百度释义 斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种.最小生成树是在给定的点集和边中寻求最短网络使所有点连通.而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小. 即最小斯坦那树即为并非选择所有的结点,而是选择一部分结点,为保证它们连通,且求解最小开销 题解 斯坦那树模板 发现直接表示点的存在性没有意义 设函数 \(f[i][state]\) 表示:对于点 \(i\),其它结点与其连通情况 那么有两种转移 其一.由其子集转移 \[f[i][state