[Luogu P4294] [BZOJ 2595] [WC2008]游览计划

48 篇文章 1 订阅
14 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

从未来过绍兴的小D有幸参加了Winter Camp 2008,他被这座历史名城的秀丽风景所吸引,强烈要求游览绍兴及其周边的所有景点。

主办者将绍兴划分为 N N N M M M ( N × M ) (N×M) (N×M)个分块,如下图 ( 8 × 8 ) (8×8) (8×8)

img

景点含于方块内,且一个方块至多有一个景点。无景点的方块视为路。

为了保证安全与便利,主办方依据路况和治安状况,在非景点的一些方块内安排不同数量的志愿者;在景点内聘请导游(导游不是志愿者)。在选择旅游方案时,保证任意两个景点之间,存在一条路径,在这条路径所经过的每一个方块都有志愿者或者该方块为景点。既能满足选手们游览的需要,又能够让志愿者的总数最少。

例如,在上面的例子中,在每个没有景点的方块中填入一个数字,表示控制该方块最少需要的志愿者数目:

img

图中用深色标出的方块区域就是一种可行的志愿者安排方案,一共需要 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 的范围规定如下

img

输入文件中的所有整数均不小于 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]=minSstat(dp[i][j][S]+dp[i][j][statS]val[i][j])dp[i][j][stat]=min(dp[i][j][stat],minxi+yj=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("");
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值