[BZOJ2595][Wc2008]游览计划
试题描述
输入
第一行有两个整数,N和 M,描述方块的数目。
接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;
否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开,
行首行末也可能有多余的空格。
输出
由 N + 1行组成。第一行为一个整数,表示你所给出的方案
中安排的志愿者总数目。
接下来 N行,每行M 个字符,描述方案中相应方块的情况:
z ‘_’(下划线)表示该方块没有安排志愿者;
z ‘o’(小写英文字母o)表示该方块安排了志愿者;
z ‘x’(小写英文字母x)表示该方块是一个景点;
注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不
一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。
输入示例
4 4 0 1 1 0 2 5 5 1 1 5 5 1 0 1 1 0
输出示例
6
数据规模及约定
对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内
题解
听说叫斯坦纳树,其实是一个状压 dp。这个 dp 挺奇怪的,大概是设 f[i][j][S] 表示包含了 (i, j) 的连通块中至少包含集合 S 中所有景点的最小代价。具体请看这里。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
using namespace std;
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 11
#define maxs 1030
#define oo 2147483647
int n, m, K, A[maxn][maxn], f[maxn][maxn][maxs];
struct Sta {
int x, y, S;
Sta(): x(0), y(0), S(0) {}
Sta(int _1, int _2, int _3): x(_1), y(_2), S(_3) {}
} fa[maxn][maxn][maxs];
bool up(Sta to, Sta from, int val) {
if(f[to.x][to.y][to.S] > val) {
f[to.x][to.y][to.S] = val, fa[to.x][to.y][to.S] = from;
return 1;
}
return 0;
}
queue <Sta> Q;
bool inq[maxn][maxn];
void init() {
memset(inq, 0, sizeof(inq));
while(!Q.empty()) Q.pop();
return ;
}
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
bool inside(Sta x) {
return 1 <= x.x && x.x <= n && 1 <= x.y && x.y <= m;
}
void expend() {
while(!Q.empty()) {
Sta u = Q.front(); Q.pop(); inq[u.x][u.y] = 0;
for(int i = 0; i < 4; i++) {
Sta v(u.x + dx[i], u.y + dy[i], u.S);
if(inside(v) && up(v, u, f[u.x][u.y][u.S] + A[v.x][v.y]) && !inq[v.x][v.y])
inq[v.x][v.y] = 1, Q.push(v);
}
}
return ;
}
bool put[maxn][maxn];
void Path(Sta u) {
if(!u.S) return ;
put[u.x][u.y] = 1;
Sta v = fa[u.x][u.y][u.S];
Path(v);
if(v.S != u.S && v.S) Path(Sta(v.x, v.y, u.S - v.S));
return ;
}
int main() {
n = read(); m = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
for(int S = 0; S < maxs; S++)
f[i][j][S] = oo;
int p = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) {
A[i][j] = read();
if(!A[i][j]) f[i][j][1<<p++] = 0;
}
int all = (1 << p) - 1;
for(int S = 1; S <= all; S++) {
init();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) {
for(int Sub = S & S - 1; Sub; Sub = S & Sub - 1)
if(f[i][j][Sub] < oo && f[i][j][S-Sub] < oo)
up(Sta(i, j, S), Sta(i, j, Sub), f[i][j][Sub] + f[i][j][S-Sub] - A[i][j]);
if(f[i][j][S] < oo) Q.push(Sta(i, j, S)), inq[i][j] = 1;
}
expend();
}
for(int i = 1; i <= n; i++) {
bool has = 0;
for(int j = 1; j <= m; j++) if(!A[i][j]) {
printf("%d\n", f[i][j][all]);
Path(Sta(i, j, all));
for(int a = 1; a <= n; a++) {
for(int b = 1; b <= m; b++)
if(!A[a][b]) putchar('x');
else if(put[a][b]) putchar('o');
else putchar('_');
putchar('\n');
}
has = 1; break;
}
if(has) break;
}
return 0;
}