@description@

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

主办者将绍兴划分为N行M列(NXM)个方块,景点含于方块内,且一个方块至多有一个景点。无景点的方块视为路。

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

现在,希望你能够帮助主办方找到一种最好的安排方案。

原题传送门。

@solution@

斯坦纳树经典题。

斯坦纳树的定义是:给定一个图与 k 个特殊点,求将这 k 个特殊点连通的最小生成树。

当 k = 点数时,就是常见的最小生成树的定义。

状压 dp:记 dp[i][s] 表示当前点为 i,已经包含了 k 个特殊点的集合为 s,最小连通代价。

第一类转移:dp[i][s] = min{dp[i][t] + dp[i][s xor t]},即两个集合取并集。

第二类转移:dp[i][s] = min{dp[j][s] + dis(i, j)},即任意加一条边。

正确性很显然,因为每一种可行方案都会被上面的过程考虑到。

第二类转移带环,但是注意到这个转移很像最短路的 “松弛” 操作,于是我们直接写个 spfa 即可。

时间复杂度(如果把 spfa 算作 O(nm) 的上界的话)为 O(n*3^k + nm*2^k)。

本题因为是点权,为了不使第一种转移出现重复,dp[i][s] 不包含 i 的点权。

@accepted code@

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std; typedef pair<int, int> pii;
#define mp make_pair
#define fi first
#define se second const int INF = (1 << 30);
const int dx[] = {0, 1, -1, 0, 0};
const int dy[] = {0, 0, 0, -1, 1}; bool inq[10][10];
int A[10][10], id[10][10], N, M, K, S;
int dp[10][10][1 << 10], pre[10][10][1 << 10];
queue<pii>que;
void run(int s) {
for(int i=0;i<N;i++)
for(int j=0;j<M;j++)
que.push(mp(i, j)), inq[i][j] = true;
while( !que.empty() ) {
pii p = que.front(); que.pop(); inq[p.fi][p.se] = false;
for(int i=1;i<=4;i++) {
int x0 = p.fi + dx[i], y0 = p.se + dy[i];
if( x0 >= N || y0 >= M || x0 < 0 || y0 < 0 ) continue;
if( dp[x0][y0][s] > dp[p.fi][p.se][s] + A[p.fi][p.se] ) {
dp[x0][y0][s] = dp[p.fi][p.se][s] + A[p.fi][p.se];
pre[x0][y0][s] = i;
if( !inq[x0][y0] ) {
que.push(mp(x0, y0));
inq[x0][y0] = true;
}
}
}
}
}
void solve() {
for(int s=1;s<S;s++) {
for(int i=0;i<N;i++)
for(int j=0;j<M;j++) {
int t = s & (s - 1);
while( t ) {
if( dp[i][j][s] > dp[i][j][t] + dp[i][j][s ^ t] ) {
dp[i][j][s] = dp[i][j][t] + dp[i][j][s ^ t];
pre[i][j][s] = -t;
}
t = s & (t - 1);
}
}
run(s);
}
}
bool tag[10][10];
void get(int x, int y, int s) {
tag[x][y] = true;
if( pre[x][y][s] > 0 )
get(x - dx[pre[x][y][s]], y - dy[pre[x][y][s]], s);
else if( pre[x][y][s] < 0 ) {
pre[x][y][s] = -pre[x][y][s];
get(x, y, pre[x][y][s]), get(x, y, s^pre[x][y][s]);
}
} int main() {
scanf("%d%d", &N, &M);
for(int i=0;i<N;i++)
for(int j=0;j<M;j++) {
scanf("%d", &A[i][j]);
if( A[i][j] == 0 ) id[i][j] = (K++);
}
S = (1 << K);
for(int i=0;i<N;i++)
for(int j=0;j<M;j++) {
for(int s=0;s<S;s++) dp[i][j][s] = INF;
if( A[i][j] == 0 ) dp[i][j][1 << id[i][j]] = 0;
}
solve();
int x, y, ans = INF;
for(int i=0;i<N;i++)
for(int j=0;j<M;j++)
if( ans > dp[i][j][S - 1] + A[i][j] )
ans = dp[i][j][S - 1] + A[i][j], x = i, y = j;
printf("%d\n", ans);
get(x, y, S - 1);
for(int i=0;i<N;i++) {
for(int j=0;j<M;j++) {
if( tag[i][j] ) {
if( A[i][j] == 0 )
putchar('x');
else putchar('o');
}
else putchar('_');
}
puts("");
}
}
/*
8 8
1 4 1 3 4 2 4 1
4 3 1 2 0 1 2 3
3 2 1 3 0 3 1 2
2 6 5 0 2 4 1 0
5 1 2 1 3 4 2 5
5 1 3 1 5 0 1 4
5 0 6 1 4 5 3 4
0 2 2 2 3 4 1 1
*/

@details@

关于第二类转移,其实也可以用 floyd 先预处理出所有点对的最短路,然后直接点对两两 O(n^2) 转移。

因为 spfa 的复杂度上界是 O(NM),对于 M 比较大的稠密图用 floyd 更快。

当然对于本题这种网格图 spfa 的复杂度和 floyd 没差,spfa 的常数说不定还要小一些。

@bzoj - 2595@ 游览计划的更多相关文章

  1. 【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)

    2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 1572  Solved: 7 ...

  2. 【BZOJ】【2595】【WC2008】游览计划

    Orz zky神犇http://blog.csdn.net/iamzky/article/details/42029921 spfa的灵活应用!(好像是求了一个叫做斯坦纳树的东西……) o(︶︿︶)o ...

  3. 【BZOJ-2595】游览计划 斯坦纳树

    2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 1518  Solved: 7 ...

  4. 【LG4294】[WC2008]游览计划

    [LG4294][WC2008]游览计划 题面 洛谷 bzoj 题解 斯坦纳树板子题. 斯坦纳树的总结先留个坑. 代码 #include <iostream> #include <c ...

  5. [bzoj2595][WC2008]游览计划/[bzoj5180][Baltic2016]Cities_斯坦纳树

    游览计划 bzoj-2595 wc-2008 题目大意:题目链接.题目连接. 注释:略. 想法:裸题求斯坦纳树. 斯坦纳树有两种转移方式,设$f[s][i]$表示联通状态为$s$,以$i$为根的最小代 ...

  6. BZOJ_2595_[Wc2008]游览计划_斯坦纳树

    BZOJ_2595_[Wc2008]游览计划_斯坦纳树 题意: 分析: 斯坦纳树裸题,有几个需要注意的地方 给出矩阵,不用自己建图,但枚举子集转移时会算两遍,需要减去当前点的权值 方案记录比较麻烦,两 ...

  7. [WC2008]游览计划 解题报告

    [WC2008]游览计划 斯坦纳树板子题,其实就是状压dp 令\(dp_{i,s}\)表示任意点\(i\)联通关键点集合\(s\)的最小代价 然后有转移 \[ dp_{i,S}=\min_{T\in ...

  8. bzoj2595 / P4294 [WC2008]游览计划

    P4294 [WC2008]游览计划 斯坦纳树 斯坦纳树,是一种神奇的树.它支持在一个连通图上求包含若干个选定点的最小生成树. 前置算法:spfa+状压dp+dfs(大雾) 我们设$f[o][P]$为 ...

  9. 【BZOJ2595】 [Wc2008]游览计划

    BZOJ2595 [Wc2008]游览计划 Solution 考虑这是一个最小费用连通性的问题,既然大家都说这是什么斯坦纳树那就是的吧... 所以我们肯定可以这样设一个dp状态: \(dp_{i,j, ...

随机推荐

  1. jsp 循环数字

    <c:forEach var ="i" begin="1" end="${homeexamque.optionNum}" step=& ...

  2. 使用interllij IDEA 写第一个Java程序

    安装interllij IDEA interllij IDEA简称IDEA,是最好用的Java集成开发环境.你只需要安装一个IDEA,就可以立马开始学习Java,不用再费心去配置Java环境. IDE ...

  3. Docker 错误:network xxx id xxxx has active endpoints

    问题描述:Docker Compose 部署的项目,使用docker-compose down 命令关闭时,提示错误: Removing network xxxl_default ERROR: net ...

  4. JUC(4)---java线程池原理及源码分析

    线程池,既然是个池子里面肯定就装很多线程. 如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁 线程,如此一来会大大降低系统的效率.可能出现服务器在为每个请求创建新线程和销毁 ...

  5. Kubernetes学习笔记(五):卷

    简介 卷是Pod的一部分,与Pod共享生命周期.它不是独立的Kubernetes对象,因此不能单独创建. 卷提供的存储功能不但可以解决容器重启后数据丢失的问题,还可以使数据在容器间共享. 一些卷的类型 ...

  6. SpringBoot外部化配置使用Plus版

    本文如有任何纰漏.错误,请不吝指正! PS: 之前写过一篇关于SpringBoo中使用配置文件的一些姿势,不过嘛,有句话(我)说的好:曾见小桥流水,未睹观音坐莲!所以再写一篇增强版,以便记录. 序言 ...

  7. 如何理解Java中的自动拆箱和自动装箱?

    小伟刚毕业时面的第一家公司就被面试官给问住了... 如何理解Java中的自动拆箱和自动装箱? 自动拆箱?自动装箱?什么鬼,听都没听过啊,这...这..知识盲区... 回到家后小伟赶紧查资料,我透,这不 ...

  8. 一文读懂JAVA多线程

    背景渊源 摩尔定律 提到多线程好多书上都会提到摩尔定律,它是由英特尔创始人之一Gordon Moore提出来的.其内容为:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍 ...

  9. 基于 kubeadm 搭建高可用的kubernetes 1.18.2 (k8s)集群 部署 dashboard 2.x

    1. 部署dashboard 2.x版本 Dashboard 分为 1.x版本 和 2.x版本, k8s 使用的是1.18.2 故部署2.x版本的 # dashboard 2.x版本的部署 # 上传d ...

  10. 华为五大专家亲述:如何转型搞 AI?

    导语:非AI专业技术人员转型AI技术,或是作为一名学生学习AI技术开发,对每个有这样诉求和经历的人来说,都希望能够看到AI技术人才的成长经历,给出自己的真实经历分享. 前言 参考塞缪尔.约翰逊(18世 ...