洛谷传送门
BZOJ传送门
题目描述
从未来过绍兴的小D有幸参加了Winter Camp 2008,他被这座历史名城的秀丽风景所吸引,强烈要求游览绍兴及其周边的所有景点。
主办者将绍兴划分为 N N N行 M M M列 ( N × M ) (N×M) (N×M)个分块,如下图 ( 8 × 8 ) (8×8) (8×8):
景点含于方块内,且一个方块至多有一个景点。无景点的方块视为路。
为了保证安全与便利,主办方依据路况和治安状况,在非景点的一些方块内安排不同数量的志愿者;在景点内聘请导游(导游不是志愿者)。在选择旅游方案时,保证任意两个景点之间,存在一条路径,在这条路径所经过的每一个方块都有志愿者或者该方块为景点。既能满足选手们游览的需要,又能够让志愿者的总数最少。
例如,在上面的例子中,在每个没有景点的方块中填入一个数字,表示控制该方块最少需要的志愿者数目:
图中用深色标出的方块区域就是一种可行的志愿者安排方案,一共需要 20 20 20名志愿者。由图可见,两个相邻的景点是直接(有景点内的路)连通的(如沈园和八字桥)。
现在,希望你能够帮助主办方找到一种最好的安排方案。
输入输出格式
输入格式:
第一行有两个整数, N N N和 M M M,描述方块的数目。
接下来 N N N行,每行有 M M M个非负整数,如果该整数为 0 0 0,则该方块为一个景点;
否则表示控制该方块至少需要的志愿者数目。相邻的整数用(若干个)空格隔开,
行首行末也可能有多余的空格。
输出格式:
由 N + 1 N+1 N+1行组成。第一行为一个整数,表示你所给出的方案中安排的志愿者总数目。
接下来 N N N行,每行 M M M个字符,描述方案中相应方块的情况:
‘_’(下划线)表示该方块没有安排志愿者;
‘o’(小写英文字母o)表示该方块安排了志愿者;
‘x’(小写英文字母x)表示该方块是一个景点;
注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不一致(任何一行中,多余的空格都不允许出现),都可能导致该测试点不得分。
输入输出样例
输入样例#1:
4 4
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
输出样例#1:
6
xoox
___o
___o
xoox
说明
所有的 10 10 10 组数据中 N , M N, M N,M ,以及景点数 K K K 的范围规定如下
输入文件中的所有整数均不小于 0 0 0 且不超过 2 16 2^{16} 216
解题分析
斯坦纳树板题。
设 d p [ i ] [ j ] [ s t a t ] dp[i][j][stat] dp[i][j][stat]表示第 i i i行第 j j j个点为根, 关键点连通情况为 s t a t stat stat的最小花费, v a l [ i ] [ j ] val[i][j] val[i][j]为这个格子的花费。
那么有两种转移:
d
p
[
i
]
[
j
]
[
s
t
a
t
]
=
m
i
n
S
⊂
s
t
a
t
(
d
p
[
i
]
[
j
]
[
S
]
+
d
p
[
i
]
[
j
]
[
∁
s
t
a
t
S
]
−
v
a
l
[
i
]
[
j
]
)
d
p
[
i
]
[
j
]
[
s
t
a
t
]
=
m
i
n
(
d
p
[
i
]
[
j
]
[
s
t
a
t
]
,
m
i
n
∣
x
−
i
∣
+
∣
y
−
j
∣
=
1
(
d
p
[
x
]
[
y
]
[
S
]
+
v
a
l
[
i
]
[
j
]
)
dp[i][j][stat]=min_{S\subset stat}(dp[i][j][S]+dp[i][j][\complement_{stat}S]-val[i][j]) \\ dp[i][j][stat]=min(dp[i][j][stat], min_{|x-i|+|y-j|=1}(dp[x][y][S]+val[i][j])
dp[i][j][stat]=minS⊂stat(dp[i][j][S]+dp[i][j][∁statS]−val[i][j])dp[i][j][stat]=min(dp[i][j][stat],min∣x−i∣+∣y−j∣=1(dp[x][y][S]+val[i][j])
上面一个枚举子集可以做到
O
(
n
m
3
k
)
O(nm3^k)
O(nm3k), 下面一个有后效性, 但可以
S
P
F
A
SPFA
SPFA直接搞出来。
总复杂度为 O ( n m 3 k ) O(nm3^k) O(nm3k)。
代码如下:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cctype>
#include <queue>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define INF 0x3f3f3f3f
int n, m, all, tot, ans;
char mp[11][11];
const int dx[4] = {-1, 0, 0, 1};
const int dy[4] = {0, -1, 1, 0};
int val[11][11];
int dp[11][11][1025];
bool vis[11][11];
struct List {int x, y, st;}lis[11][11][1025];
struct Cood {int x, y;};
std::queue <Cood> q;
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
IN void SPFA(R int stat)
{
Cood cur;
R int nx, ny;
W (!q.empty())
{
cur = q.front(); q.pop();
for (R int i = 0; i < 4; ++i)
{
nx = cur.x + dx[i], ny = cur.y + dy[i];
if (nx < 1 || ny < 1 || nx > n || ny > m) continue;
if (dp[nx][ny][stat] > dp[cur.x][cur.y][stat] + val[nx][ny])
{
dp[nx][ny][stat] = dp[cur.x][cur.y][stat] + val[nx][ny];
lis[nx][ny][stat] = {cur.x, cur.y, stat};
if (!vis[nx][ny]) q.push({nx, ny});
}
}
vis[cur.x][cur.y] = false;
}
}
void DFS(R int x, R int y, R int stat)
{
vis[x][y] = true;
if (lis[x][y][stat].x == 0) return;
DFS(lis[x][y][stat].x, lis[x][y][stat].y, lis[x][y][stat].st);
if (lis[x][y][stat].x == x && lis[x][y][stat].y == y)
DFS(lis[x][y][stat].x, lis[x][y][stat].y, stat ^ lis[x][y][stat].st);
}
int main(void)
{
in(n), in(m); R int i, j, k, s, tx, ty;
std::memset(dp, 63, sizeof(dp));
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
{
in(val[i][j]);
if (!val[i][j])
dp[i][j][1 << tot] = 0, ++tot, tx = i, ty = j;
}
all = (1 << tot) - 1;
for (s = 1; s <= all; ++s)
{
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
{
for (k = s & (s - 1); k; k = (k - 1) & s)
{
if (dp[i][j][s] > dp[i][j][k] + dp[i][j][s ^ k] - val[i][j])
{
dp[i][j][s] = dp[i][j][k] + dp[i][j][s ^ k] - val[i][j];
lis[i][j][s] = {i, j, k};
}
}
if (dp[i][j][s] != INF) q.push({i, j}), vis[i][j] = true;
}
SPFA(s);
}
DFS(tx, ty, all);
for (R int i = 1; i <= n; ++i)
for (R int j = 1; j <= m; ++j)
ans += vis[i][j] * val[i][j];
printf("%d\n", ans);
for (R int i = 1; i <= n; ++i)
{
for (R int j = 1; j <= m; ++j)
{
if (!val[i][j]) putchar('x');
else if (vis[i][j]) putchar('o');
else putchar('_');
}
puts("");
}
}