[BZOJ]2595 [WC2008] 遊覽計劃 斯坦納樹

2595: [Wc2008]遊覽計劃

Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special Judge
Submit: 1862  Solved: 905
[Submit][Status][Discuss]

Description

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]的範圍內

Source

[Submit][Status][Discuss]


HOME Back

  一直以爲斯坦納樹是數據結構... 於是去學了一發, 發現是跟最小生成樹有點像的東西. 斯坦納樹就是來解決將多個欽定的關鍵點連接成樹的問題. 而如果有點權(邊權), 一般是求最小(大)斯坦納樹(權值和最小/大). 譬如這道題就是求最小斯坦納樹. 這類問題實際上是NPC問題... 不過在數據範圍較小的情況下可以用狀壓搞搞解決.

  實際上斯坦納樹是用了dp. 拿這道題來舉例, 我們設f[i][j][s]爲以(i, j)這個點爲根此時連通性爲s的最小權值和. 那麼考慮如何轉移.

  首先可以由子集轉移過來. f[i][j][sta] = min(f[i][j][sta], f[i][j][s] + f[i][j][sta ^ s] - a[i][j])(-a[i][j]是因爲重算了根). 對於子集枚舉技巧可見代碼.

  其次顯然某棵樹可以由某棵樹加邊而來. f[i][j][sta] =min(f[i][j][sta], f[x][y][sta] + a[i][j]). 我們會發現這個很像三角形不等式, 於是spfa即可. 這裏只對當前狀態進行鬆弛. 因爲其他的聯通狀態都可由上一層子集轉移來更新. 所以只用對當前進行spfa辣.

  通過以上兩種轉移就可以搞出斯坦納樹了, excited! 記錄方案就非常的naive了. 總的來說十分好寫. 第一次寫斯坦納樹就從黃學長那裏借(fang)鑑(chao)了一下. 發現仿抄速度快過香港記者

  Ps: 自作聰明的還搞了個尋址優化才發現自己根本不懂什麼叫尋址優化啊... 改天好好學一發卡常.

#include<bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define fufil(a) memset(a, 0x3f, sizeof(a))
using namespace std;
const int inf = 1e9;
const int maxn = 12;
const int maxm = (1 << 10) + 5;
bool ans[maxn][maxn], vis[maxn][maxn];
int n, m, k, stx, sty, a[maxn][maxn], f[maxn][maxn][maxm];
struct rd {
    int x, y, sta;
    rd(){}
    rd(int a, int b, int c) : x(a), y(b), sta(c) {}
}pr[maxn][maxn][maxm];
queue<pair<int, int> > q;
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
inline void spfa(int s) {
    while (!q.empty()) {
        int x = q.front().xx, y = q.front().yy;
        vis[x][y] = false, q.pop();
        for (int k = 0; k < 4; ++ k) {
            int nx = x + dx[k], ny = y + dy[k];
            if (nx < 1 || nx > n || ny < 1 || ny > m) continue;
            if (f[nx][ny][s] > f[x][y][s] + a[nx][ny]) {
                f[nx][ny][s] = f[x][y][s] + a[nx][ny];
                pr[nx][ny][s] = rd(x, y, s);
                if (!vis[nx][ny]) vis[nx][ny] = true, q.push(mp(nx, ny));
            }
        }
    }
}
void dfs(int x, int y, int sta) {
    ans[x][y] = true;
    rd t = pr[x][y][sta];
    if (!t.x && !t.y) return;
    dfs(t.x, t.y, t.sta);
    if (t.x == x && t.y == y) dfs(t.x, t.y, sta ^ t.sta);
}
int main() {
    fufil(f);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++ i)
        for (int j = 1; j <= m; ++ j) {
            scanf("%d", &a[i][j]);
            if (!a[i][j]) f[i][j][1 << k] = 0, k ++, stx = i, sty = j;
        }
    int lim = 1 << k;
    for (int sta = 0; sta < lim; ++ sta) {
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= m; ++ j) {
                for (int s = sta & (sta - 1); s; s = sta & (s - 1)) {
                    int nw = f[i][j][s] + f[i][j][sta ^ s] - a[i][j];
                    if (nw < f[i][j][sta]) {
                        f[i][j][sta] = nw;
                        pr[i][j][sta] = rd(i, j, s);
                    }
                }
                if (f[i][j][sta] < inf) q.push(mp(i, j)), vis[i][j] = true;
            }
        spfa(sta);
    }
    printf("%d\n", f[stx][sty][lim - 1]);
    dfs(stx, sty, lim - 1);
    for (int i = 1; i <= n; ++ i, puts(""))
        for (int j = 1; j <= m; ++ j) {
            if (!a[i][j]) putchar('x');
            else if (ans[i][j]) putchar('o');
            else putchar('_');
        }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章