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

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=2595
【题解】
  斯坦纳树模板题。
  记 f i , j , k 表示当前最小生成树的根在 ( i , j ) ,连在这棵树上的关键点(景点)的状态为 k ,(k是一个二进制数,表示关键点是否与根连通)。
  转移分为当前层转移和跨层转移,当前层转移就是在 k 不变的状态下用spfa更新答案。跨层转移就是在 ( i , j ) 不变的情况下用枚举子集的方式更新答案。
  具体来说就是:
  1) f i , j , k = m i n ( f t , p , k + d i s ( t , p ) , ( i , j ) ) ( 1 t n , 1 p m )
  2) f i , j , k = m i n ( f i , j , t + f i , j , k t ) ( t k )
  为了输出方案,每个状态再记录通过哪个方式的哪个状态转移过来,回溯即可。
  时间复杂度 O ( N 2 3 N + 2 N M k ) ( k s p f a )
【代码】

/* - - - - - - - - - - - - - - -
    User :      VanishD
    problem :   [bzoj2595] 
    Points :    Steiner Tree 
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       11
# define    M       110
# define    K       10
using namespace std;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
    return tmp * fh;
}
int use[N][N], qx[M], qy[M], f[N][N][1 << K], tag[N][N][1 << K], frmx[N][N][1 << K], frmy[N][N][1 << K], n, m, mp[N][N], cntx[N], cnty[N], id, to[N][N]; 
const int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
void spfa(int p){
    int pl = 1, pr = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++){
            use[i][j] = true;
            qx[++pr] = i; qy[pr] = j;
        }
    while (pl <= pr){
        int nowx = qx[pl % M], nowy = qy[(pl++) % M];
        for (int i = 0; i < 4; i++){
            int tmpx = nowx + dx[i], tmpy = nowy + dy[i];
            if (tmpx < 1 || tmpx > n || tmpy < 1 || tmpy > m) continue;
            if (f[tmpx][tmpy][p] > f[nowx][nowy][p] + mp[tmpx][tmpy]){
                f[tmpx][tmpy][p] = f[nowx][nowy][p] + mp[tmpx][tmpy];
                tag[tmpx][tmpy][p] = 1, frmx[tmpx][tmpy][p] = nowx, frmy[tmpx][tmpy][p] = nowy;
                if (use[tmpx][tmpy] == false){
                    use[tmpx][tmpy] = true;
                    qx[(++pr) % M] = tmpx; qy[pr % M] = tmpy; 
                }
            }
        }
        use[nowx][nowy] = false;
    }
}
void solve(int x, int y, int tmp){
    if (tag[x][y][tmp] == -1) return;
    if (tag[x][y][tmp] == 1){
        to[x][y] = true;
        solve(frmx[x][y][tmp], frmy[x][y][tmp], tmp);
    }
    else {
        solve(x, y, frmx[x][y][tmp]);
        solve(x, y, frmy[x][y][tmp]);
    }
}
int main(){
//  freopen("bzoj2595.in", "r", stdin);
//  freopen("bzoj2595.out", "w", stdout);
    n = read(), m = read();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++){
            mp[i][j] = read(); 
            if (mp[i][j] == 0){
                cntx[id] = i, cnty[id] = j;
                id++;
            }
        }
    memset(f, inf, sizeof(f));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++){
            f[i][j][0] = 0;
            tag[i][j][0] = -1;
        }   
    for (int i = 0; i < id; i++){
        f[cntx[i]][cnty[i]][1 << i] = 0;
        tag[cntx[i]][cnty[i]][1 << i] = -1;
    }   
    for (int i = 0; i < (1 << id); i++){
        for (int j = 1; j <= n; j++)
            for (int k = 1; k <= m; k++)
                for (int t = i; t > 0; t = (t - 1) & i){
                    if (f[j][k][i] > f[j][k][t] + f[j][k][i - t] - mp[j][k]){
                        f[j][k][i] = f[j][k][t] + f[j][k][i - t] - mp[j][k];
                        tag[j][k][i] = 0;
                        frmx[j][k][i] = t; frmy[j][k][i] = i - t;
                    }
                }
        spfa(i);
    }
    int ans = inf, ansx, ansy;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++){
            int tmp = f[i][j][(1 << id) - 1];
            if (ans > tmp){
                ansx = i, ansy = j;
                ans = tmp;
            }
        }
    printf("%d\n", ans);
    solve(ansx, ansy, (1 << id) - 1);
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++)
            if (mp[i][j] == 0) printf("x");
                else if (to[i][j] == true) printf("o");
                    else printf("_");
        printf("\n");
    } 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/d_vanisher/article/details/80752496
今日推荐