P4294 [WC2008]游览计划

斯坦纳树

斯坦纳树,是一种神奇的树。它支持在一个连通图上求包含若干个选定点的最小生成树

前置算法:spfa+状压dp+dfs(大雾)


我们设$f[o][P]$为第$o$个点上状态为$P$的最小代价,其中状态使用二进制存储已经连接了多少个选定点。

初始化:显然对于每个选定点,$f[o][1<<k]=0$,$k$为该选定点在所有选定点中的编号。其他为$inf$


蓝后就是将状态从小到大枚举进行递推

$for(P=0;P<(1<<k);++P)$

对于每层递推,枚举所有点$1$~$o$;

我们先考虑这个点连接了2个不同连通块(链)的状况(并为spfa做好准备

于是我们枚举$P$的真子集进行递推

$for(j=(P-1)\&P;j;j=(j-1)\&P)$

枚举真子集↑

$f[o][P]=min(f[o][P],f[o][j]+f[o][P$^$j]-val[o]);$

注意该式适用于计算点权,减去$val[o]$是去掉重复点权。如果计算边权需作修改。


但是这样显然远远不够。于是我们用$spfa$通过类似$dp$的形式处理好剩下的所有状况。

对于前面的$f[o][P]$,任何$f[o][P]<inf$都应作为每层spfa的起点(显然spfa也是每层执行)


在共$K$个给定点中,随意找一个给定点作为树根$rt$。

$ans=f[rt][(1<<K)-1]$


对于本题$(extra)$:输出一种方案

我们在每次更新$f[o][P]$时

用$fa[o][P]$记下$f[o][P]$从哪个状态推导而来

最后从树根$rt$用$dfs$倒推回去,更新答案即可。

void dfs(pii u,int o){//u:坐标
par G=fa[F(u)][o];
if(!G.se) return;
use[u.fi][u.se]=;
if(G.fi==u) dfs(u,o^G.se);//两个联通块合并而来,则要从两条路递推回去。
dfs(G.fi,G.se);
}

$code:$

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int,int> pii;
typedef pair<pii,int> par;
#define mp make_pair
#define fi first
#define se second
int d1[]={,,,-};
int d2[]={,,-,};
int n,m,tot,k,inf,f[][],id[][],a[][];
bool in[][],use[][];
par fa[][]; pii rt;
queue <pii> h;
inline int F(pii x){return id[x.fi][x.se];}
void spfa(int o){
while(!h.empty()){
pii x=h.front(); h.pop(); in[x.fi][x.se]=;
for(int i=;i<;++i){
int r1=x.fi+d1[i],r2=x.se+d2[i],to=id[r1][r2];
if(r1<||r1>=n||r2<||r2>=m) continue;
if(f[to][o]>f[F(x)][o]+a[r1][r2]){
f[to][o]=f[F(x)][o]+a[r1][r2];
fa[to][o]=mp(x,o);
if(!in[r1][r2]) h.push(mp(r1,r2));
}
}
}
}
void dfs(pii u,int o){//逆推找可行方案
par G=fa[F(u)][o];
if(!G.se) return;
use[u.fi][u.se]=;//选择点u
if(G.fi==u) dfs(u,o^G.se);
dfs(G.fi,G.se);
}
int main(){
memset(f,,sizeof(f)); inf=f[][];
scanf("%d%d",&n,&m);
for(int i=;i<n;++i)
for(int j=;j<m;++j){
scanf("%d",&a[i][j]);
id[i][j]=++tot;
if(!a[i][j]) rt=mp(i,j),f[tot][<<k]=,++k;
}
for(int P=;P<(<<k);spfa(P),++P)//每层递推的最后来一次spfa
for(int x=;x<n;++x)
for(int y=;y<m;++y){
int o=id[x][y];
for(int j=(P-)&P;j;j=(j-)&P)//枚举真子集
if(f[o][P]>f[o][j]+f[o][P^j]-a[x][y]){
f[o][P]=f[o][j]+f[o][P^j]-a[x][y];
fa[o][P]=mp(mp(x,y),j);
}
if(f[o][P]<inf) h.push(mp(x,y)),in[x][y]=;//可行点都能spfa
}
printf("%d\n",f[F(rt)][(<<k)-]); dfs(rt,(<<k)-);
for(int i=;i<n;++i,printf("\n"))
for(int j=;j<m;++j){
if(!a[i][j]) printf("x");
else if(use[i][j]) printf("o");
else printf("_");
}
return ;
}

bzoj2595 / P4294 [WC2008]游览计划的更多相关文章

  1. 【BZOJ2595】[Wc2008]游览计划 斯坦纳树

    [BZOJ2595][Wc2008]游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为 ...

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

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

  3. luogu P4294 [WC2008]游览计划

    LINK:游览计划 斯坦纳树例题. 斯坦纳树是这样一类问题:带权无向图上有K个关键点 求出包含这K个点的最小生成树. 也就是说 求最小生成树 但是 并不是整张图 仅限于K个点. 可以发现我们利用克鲁斯 ...

  4. 洛谷 P4294 [WC2008]游览计划

    题目链接 不是很会呢,但似乎抄了题解后有点明白了 sol:状态DP显然,其实是要构建一棵最小生成树一样的东西,我自己的理解(可能不是很对哦希望多多指教)f[x][y][zt]就是到x,y这个点,状态为 ...

  5. BZOJ2595:[Wc2008]游览计划——题解(插头dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=2595 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行 ...

  6. P4294 [WC2008]游览计划

    传送门 斯坦纳树 给一个联通图,求 $k$ 个关键点联通的最小生成树权值 设 $f[o][i]$ 表示当前关键点选择状态为 $o$ ,以点 $i$ 为根的树的最小权值 初始 $f[1<<( ...

  7. P4294 [WC2008]游览计划 (斯坦纳树)

    题目链接 差不多是斯坦纳树裸题,不过边权化成了点权,这样在合并两棵子树时需要去掉根结点的权值,防止重复. 题目还要求输出解,只要在转移时记录下路径,然后dfs一遍就好了. #include<bi ...

  8. BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*

    BZOJ2595 Wc2008 游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个 ...

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

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

随机推荐

  1. es6 数组..... ==和===的区别 es6的递归方式 es6find函数 timer setTimeout v-html的用法,-

    相当于push了  find函数来找到某个值 如果新建一个 setTimeout 的timer 首先得清除这个timer. v-html用法之一就是加载后台传过来的模板

  2. 合理利用配置不同的机器资源做redis cluster的server

    Redis cluster可以使用不同配置的机器学习因为我们可以手动调整不同的机器所承担的slot的个数,这样内存小CPU相对少的机器应该承担更少的slots

  3. 一个toolkit或者一个开源项目如何学习它并使用它

    一个toolkit或者一个开源项目如何学习它并使用它 一般一个流行的toolkit和开源项目,一般都会被广泛地被应用: 那么,我们如何学习它,如何应用它在自己的业务场景中呢? 答案就是:学习源码并借鉴 ...

  4. (转载)常用正则表达式大全!(例如:匹配中文、匹配html)

    正则匹配java注意点: 如果加 ^[\n]* 表示替换遇到 \n 的前后内容,如果加[\n]表示替换\n本处内容 原文地址:http://blog.csdn.net/dl020840504/arti ...

  5. springboot整合webSocket的使用

    引入jar包 <dependency><!-- 5.引入websocket--> <groupId>org.springframework.boot</gro ...

  6. 步进电机 28BYJ-48介绍和驱动及编程

    28BYJ-48步进电机: 步进电机是一种将电脉冲转化为角位移的执行机构.通俗一点讲:当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(及步进角).您可以通过控制脉冲个来 ...

  7. 【Elasticsearch学习之二】Elasticsearch Rest风格操作

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk8 elasticsearch-2.2.0 一.Rest简介Re ...

  8. zookeeper开发

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk8 zookeeper-3.4.11 ZK客户端操作命令: #登 ...

  9. flask 使用宏渲染表单(包含错误信息)

    在模板中渲染表单时,有大量的工作: 1.调用字段属性,获取<input>定义 2.调用对应的label属性,获取<label>定义 3.渲染错误消息 为了避免为每一个字段重复这 ...

  10. 转:Http下载文件类 支技断点续传功能

    using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Net ...