首页 > 代码库 > bzoj2595 [Wc2008]游览计划

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;
}
View Code

 

bzoj2595 [Wc2008]游览计划