BZOJ2595(状压dp)

要点

  • \(f[i][j][k]\)经过点\((i,j)\)且包含点集\(k\)的最小代价,其中k是指景点集合的枚举。
  • 考虑有两种情况:1.点\((i,j)\)作为关键点连接了两个子集时\(f[i][j][k]\)可以得到最小,有\(f[i][j][k]=f[i][j][k_1]+f[i][j][k_2]-a[i][j],\ k_1|k_2=k\);2.点\((i,j)\)作为主干道发展出来的枝叶时\(f[i][j][k]\)可以得到最小,那么它是它的邻居发展过来的,spfa跑一下即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

const int inf = 0x3f3f3f3f;
const int xx[] = {0, 0, -1, 1};
const int yy[] = {1, -1, 0, 0};

int N, M, K, a[11][11];
int f[11][11][1 << 11], pre[11][11][1 << 11][3], vis[11][11];
queue< pair<int, int> > Q;

void dfs(int i, int j, int U) {
    if (!U) return;
    vis[i][j] = 1;
    int a = pre[i][j][U][0], b = pre[i][j][U][1], c = pre[i][j][U][2];
    dfs(a, b, c);
    if (i == a && j == b)   dfs(a, b, c ^ U);
}

void output() {
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++)
            if (!a[i][j])   putchar('x');
            else if (vis[i][j]) putchar('o');
            else    putchar('_');
        puts("");
    }
}

int main() {
    scanf("%d %d", &N, &M);
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= M; j++) {
            for (int k = 0; k < (1 << 10); k++) {
                f[i][j][k] = inf;
            }
            scanf("%d", &a[i][j]);
            if (!a[i][j]) {
                f[i][j][1 << (K++)] = 0;
            }
        }
    for (int U = 1; U < (1 << K); U++) {
        memset(vis, 0, sizeof vis);
        //作为枝干的转移
        for (int i = 1; i <= N; i++)
            for (int j = 1; j <= M; j++) {
                for (int k = U & (U - 1); k; k = U & (k - 1)) {//枚举二进制真子集
                    int tmp = f[i][j][k] + f[i][j][k ^ U] - a[i][j];
                    if (tmp < f[i][j][U]) {
                        f[i][j][U] = tmp;
                        pre[i][j][U][0] = i;
                        pre[i][j][U][1] = j;
                        pre[i][j][U][2] = k;
                    }
                }
                if (f[i][j][U] < inf)   Q.push({i, j}), vis[i][j] = 1;
            }
        //作为树枝的转移
        while (Q.size()) {
            int x = Q.front().first, y = Q.front().second; Q.pop();
            vis[x][y] = 0;
            for (int t = 0; t < 4; t++) {
                int nx = x + xx[t], ny = y + yy[t];
                if (nx < 1 || nx > N || ny < 1 || ny > M)   continue;
                if (f[nx][ny][U] > f[x][y][U] + a[nx][ny]) {
                    f[nx][ny][U] = f[x][y][U] + a[nx][ny];
                    pre[nx][ny][U][0] = x;
                    pre[nx][ny][U][1] = y;
                    pre[nx][ny][U][2] = U;
                    if (!vis[nx][ny])   Q.push({nx, ny}), vis[nx][ny] = 1;
                }
            }
        }
    }
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= M; j++)
            if (!a[i][j]) {
                memset(vis, 0, sizeof vis);
                dfs(i, j, (1 << K) - 1);
                printf("%d\n", f[i][j][(1 << K) - 1]);
                output();
                return 0;
            }
}

猜你喜欢

转载自www.cnblogs.com/AlphaWA/p/10803806.html