# 【BZOJ2595】【WC2008】游览计划

【题目链接】

【思路要点】

• 可以直接用基于连通性的的动态规划求解，或者可以用斯坦纳树DP+SPFA求解。
• 时间复杂度$$O(N*M*3^K)$$。

【代码】

#include<bits/stdc++.h>
using namespace std;
#define MAXN	15
#define MAXQ	2000005
#define CURR	1024
#define INF	1e9
struct pos {int x, y, curr; };
int ans[MAXN][MAXN][CURR];
pos path[MAXN][MAXN][CURR];
int n, m, k, value[MAXN][MAXN], num[MAXN][MAXN];
bool inq[MAXN][MAXN][CURR], mark[MAXN][MAXN];
void work(pos home) {
mark[home.x][home.y] = true;
if (path[home.x][home.y][home.curr].curr == 0) return;
pos tmp = path[home.x][home.y][home.curr];
if (home.curr == tmp.curr) work(tmp);
else work(tmp), work((pos) {tmp.x, tmp.y, home.curr ^ tmp.curr});
}
void update(int pi, int pj, int pc, int ti, int tj, int tc, int delta) {
int tmp = ans[ti][tj][tc] + delta;
if (tmp < ans[pi][pj][pc]) {
ans[pi][pj][pc] = tmp;
path[pi][pj][pc] = (pos) {ti, tj, tc};
}
}
void push(pos a) {
inq[a.x][a.y][a.curr] = true;
}
void pop(pos a) {
inq[a.x][a.y][a.curr] = false;
}
bool query(pos a) {
return inq[a.x][a.y][a.curr];
}
int main() {
cin >> n >> m; k = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
cin >> value[i][j];
if (value[i][j] == 0) num[i][j] = k++;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
for (int curr = 1; curr < 1 << k; curr++)
if (value[i][j] == 0 && curr == 1 << num[i][j]) ans[i][j][curr] = 0;
else ans[i][j][curr] = INF;
for (int curr = 1; curr < 1 << k; curr++) {
static pos q[MAXQ];
int l = 0, r = -1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
for (int l = (curr - 1) & curr; l; l = (l - 1) & curr)
update(i, j, curr, i, j, l, ans[i][j][curr ^ l] - value[i][j]);
if (ans[i][j][curr] < INF) {
q[++r] = (pos) {i, j, curr};
push(q[r]);
}
}
while (l <= r) {
pos tmp = q[l++];
pop(tmp);
if (ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x][tmp.y + 1] < ans[tmp.x][tmp.y + 1][tmp.curr]) {
ans[tmp.x][tmp.y + 1][tmp.curr] = ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x][tmp.y + 1];
path[tmp.x][tmp.y + 1][tmp.curr] = tmp;
if (!query((pos) {tmp.x, tmp.y + 1, tmp.curr})) {
q[++r] = (pos) {tmp.x, tmp.y + 1, tmp.curr};
push(q[r]);
}
}
if (ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x][tmp.y - 1] < ans[tmp.x][tmp.y - 1][tmp.curr]) {
ans[tmp.x][tmp.y - 1][tmp.curr] = ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x][tmp.y - 1];
path[tmp.x][tmp.y - 1][tmp.curr] = tmp;
if (!query((pos) {tmp.x, tmp.y - 1, tmp.curr})) {
q[++r] = (pos) {tmp.x, tmp.y - 1, tmp.curr};
push(q[r]);
}
}
if (ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x + 1][tmp.y] < ans[tmp.x + 1][tmp.y][tmp.curr]) {
ans[tmp.x + 1][tmp.y][tmp.curr] = ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x + 1][tmp.y];
path[tmp.x + 1][tmp.y][tmp.curr] = tmp;
if (!query((pos) {tmp.x + 1, tmp.y, tmp.curr})) {
q[++r] = (pos) {tmp.x + 1, tmp.y, tmp.curr};
push(q[r]);
}
}
if (ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x - 1][tmp.y] < ans[tmp.x - 1][tmp.y][tmp.curr]) {
ans[tmp.x - 1][tmp.y][tmp.curr] = ans[tmp.x][tmp.y][tmp.curr] + value[tmp.x - 1][tmp.y];
path[tmp.x - 1][tmp.y][tmp.curr] = tmp;
if (!query((pos) {tmp.x - 1, tmp.y, tmp.curr})) {
q[++r] = (pos) {tmp.x - 1, tmp.y, tmp.curr};
push(q[r]);
}
}
}
}
int finalans = INF, goal = (1 << k) - 1;
pos home;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (ans[i][j][goal] < finalans) {
finalans = ans[i][j][goal];
home = (pos) {i, j, goal};
}
cout << finalans << endl;
work(home);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
if (mark[i][j]) {
if (value[i][j]) cout << 'o';
else cout << 'x';
} else cout << '_';
cout << endl;
}
return 0;
}