BZOJ2595:[Wc2008]游览计划——题解(插头dp)
http://www.lydsy.com/JudgeOnline/problem.php?id=2595
Description
Input
第一行有两个整数,N和 M,描述方块的数目。
接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;
否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开,
行首行末也可能有多余的空格。Output
由 N + 1行组成。第一行为一个整数,表示你所给出的方案
中安排的志愿者总数目。
接下来 N行,每行M 个字符,描述方案中相应方块的情况:
z ‘_’(下划线)表示该方块没有安排志愿者;
z ‘o’(小写英文字母o)表示该方块安排了志愿者;
z ‘x’(小写英文字母x)表示该方块是一个景点;
注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不
一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。Sample Input
4 4
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0Sample Output
6
xoox
___o
___o
xoox
参(抄)考(了):http://www.sigongzi.org/index.php/archives/jiyuliantongxingdezhuangtaiyasuodongtaiguihua.html
……原本也不想抄的啊,不然括号表达法还得学独立插头……
打眼一看就是一个插头dp,于是我们选用最小表示法求解。插头dp详见这里。
但是我们最开始学的插头dp只能解决哈密顿回路啊根本没有这道题这么复杂这可怎么办?
别慌,我们慢慢理:
1.显然景点是必须要取的(插头不能为“0”)。
2.显然路径必须是互相联通的线段。
对于1,我们强制不让这个格子不放插头即可。
对于2,我们考虑如果我们当前格子的上插头在轮廓线再没有与之相连通的插头,且同时我们已经扫完了所有景点,此时我们不选当前格子的话我们就将分开成两条路径了,所以必须要取;但是如果是左插头的话如果不取的话是可能符合2的。
然后正常插头dp即可。
(写这道题写了一天的我……)
(如果你插头dpA了这道题可以试一下这个数据):
4 3
0 1 1
10 10 1
10 10 0
10 10 1
ans:
3
xoo
__o
__x
___
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=;
const int mod=;
const int N=;
const int M=;
struct node{//哈希表
int nxt;
int state,ans,pos,pre;//状态,答案,编号,前一个状态
bool choose;//是否选择该方块
}edge[M];
int head[mod+],cnt;
int lcnt,rcnt;
void insert(int now,int num,int ppos,int last,bool flag){
int u=now%mod;
for(int i=head[u];i;i=edge[i].nxt){
if(edge[i].state==now){
if(num<edge[i].ans){
edge[i].ans=num;
edge[i].pos=ppos;
edge[i].pre=last;
edge[i].choose=flag;
}
return;
}
}
edge[++cnt].nxt=head[u];
head[u]=cnt;
edge[cnt].state=now;
edge[cnt].ans=num;
edge[cnt].pos=ppos;
edge[cnt].pre=last;
edge[cnt].choose=flag;
return;
}
bool g[N][N];
int w[N],cntt;
int n,m,lastedge,e1,e2;
int mp[N][N];
int mapp[N];
inline void decode(int now){
for(int i=m;i>=;i--){
w[i]=now&;
now>>=;
}
return;
}
inline int encode(){
int x=,tot=;
memset(mapp,-,sizeof(mapp));
mapp[]=;
for(int i=;i<=m;i++){
if(mapp[w[i]]==-)mapp[w[i]]=++tot;
w[i]=mapp[w[i]];
x=x<<|w[i];
}
return x;
}
inline void init(){
memset(head,,sizeof(head));
lcnt=rcnt+;rcnt=cnt;
return;
}
inline void getans(){
for(int k=lcnt;k<=rcnt;k++){
int now=edge[k].state;
int num=edge[k].ans;
decode(now);
bool flag=;
for(int l=;l<=m&&flag;l++){
if(w[l]>)flag=;
}
if(num<cntt&&flag){
cntt=num;
lastedge=k;
}
}
return;
}
void plugdp(){
insert(,,,,);
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
init();
if(i>e1||(i==e1&&j>e2))getans();//当前状态可能构成一种方案
for(int k=lcnt;k<=rcnt;k++){
int now=edge[k].state;
int num=edge[k].ans;
decode(now);
int is_right=w[j-];//这个格子左面格子的右插头
int is_down=w[j];//这个格子上面格子的下插头
bool flag=;//判断是否可以放插头,1可以不放
if(!mp[i][j])flag=;//景点必须放
else if(!is_down)flag=;//下插头不需要再匹配,故可以不放。
else{
flag=;
for(int l=;l<=m&&!flag;l++){
if(l!=j&&w[l]==is_down)flag=;
//上插头已经与轮廓线的一端匹配,可以在那一端延伸。
}
//没有匹配的插头,必须要接上
}
if(flag){//不放插头
w[j]=;//下插头没有用了
insert(encode(),num,(i-)*m+j,k,);
w[j]=is_down;
}
//放插头
if(!is_right&&!is_down)w[j]=;//一个新插头
else if(!is_down&&is_right)w[j]=is_right;//右插头延续过来
else if(is_right!=is_down&&is_right&&is_down){
for(int l=;l<=j;l++){//不太好说,但画个图应该就理解了
if(w[l]==is_right)w[l]=is_down;
}
}
//如果下插头和右插头匹配那么就把它们连起来
insert(encode(),num+mp[i][j],(i-)*m+j,k,);
}
}
}
init();
getans();
return;
}
void getans(int k){
if(!k)return;
int r=edge[k].pos;
int l=(r-)/m+;r=(r-)%m+;
g[l][r]=edge[k].choose;
getans(edge[k].pre);
return;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
scanf("%d",&mp[i][j]);
if(!mp[i][j])e1=i,e2=j;
}
}
cntt=INF;
plugdp();
printf("%d\n",cntt);
getans(lastedge);
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
if(!mp[i][j])putchar('x');
else if(g[i][j])putchar('o');
else putchar('_');
}
putchar('\n');
}
return ;
}
BZOJ2595:[Wc2008]游览计划——题解(插头dp)的更多相关文章
- 斯坦纳树 [bzoj2595][wc2008]游览计划 题解
话说挺早就写过斯坦纳树了,不过当时没怎么总结,也不是很理解……现在来个小结吧~ 斯坦纳树就是包含给定点的最小生成树(个人理解权值应当为正). 一般来讲,给定点的数目应该很小吧...于是我们可以用状压D ...
- BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*
BZOJ2595 Wc2008 游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个 ...
- [bzoj2595][WC2008]游览计划/[bzoj5180][Baltic2016]Cities_斯坦纳树
游览计划 bzoj-2595 wc-2008 题目大意:题目链接.题目连接. 注释:略. 想法:裸题求斯坦纳树. 斯坦纳树有两种转移方式,设$f[s][i]$表示联通状态为$s$,以$i$为根的最小代 ...
- [WC2008]游览计划(状压dp)
题面太鬼畜不粘了. 题意就是给一张n*m的网格图,每个点有点权,有k个关键点,让你把这k个关键点连成一个联通快的最小代价. 题解 这题nmk都非常小,解法肯定是状压,比较一般的解法插头dp,但不太好写 ...
- BZOJ2595 [Wc2008]游览计划 【状压dp + 最短路】
题目链接 BZOJ2595 题解 著名的斯坦纳树问题 设\(f[i][j][s]\)表示点\((i,j)\)与景点联通状况为\(s\)的最小志愿者数 设\(val[i][j]\)为\((i,j)\)需 ...
- BZOJ2595: [Wc2008]游览计划(斯坦纳树,状压DP)
Time Limit: 10 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 2030 Solved: 986[Submit][Status][ ...
- BZOJ2595[WC2008]游览计划
Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数 ...
- BZOJ2595 WC2008游览计划(斯坦纳树)
斯坦纳树板子题. 考虑状压dp,设f[i][j][S]表示当前在点(i,j)考虑转移,其所在的联通块包含的关键点集(至少)为S的答案. 转移时首先枚举子集,有f[i][j][S]=min{f[i][j ...
- luogu4294 [WC2008]游览计划(状压DP/斯坦纳树)
link 题目大意:给定一个网格图,有些点是关键点,选择格点有代价,求把所有关键点联通的最小代价 斯坦纳树模板题 斯坦纳树问题:给定一个图结构,有一些点是关键点,求把这些关键点联通的最小代价e 斯坦纳 ...
随机推荐
- char和String 在jsp java代码中与jstl代码中的区别
在 jsp java代码中 '0' ,这种代表char 在jstl中 '0' 会被解释为 String 所以也可以用 .equals 方法
- 车牌,车架号,VIN码毫秒识别技术,汽车后市场的春天到来了
vin码(车架号)识别运用 不仅在制造.销售.保养.保险.车辆评估.交易环节会需要录入汽车的VIN码,在交通事故处理中,作为汽车身份唯一识别码,VIN码是处理事故的执法人员必须要记录的信息之一.随着汽 ...
- php常用的几个预定义变量
__FILE__:返回所在路径文件名和文件名称 __DIR__:返回文件所在的完整目录 __LINE__:返回当前文件代码的行号 __CLASS__:返回当前类名 __FUNCTION__:返回当前方 ...
- shell 判断日期间隔及润年
#!/bin/bash test.sh until echo "----------------------------------" echo "请输入您的选择:&qu ...
- AnyProxy对搜狐汽车app抓包
关于AnyProxy 详细文档链接 http://anyproxy.io/cn/ anyproxy流程图 简要描述 当http请求经过代理服务器时,具体处理过程是: 收集请求所有请求参数,包括meth ...
- Spring Cloud(一):服务治理技术概览【Finchley 版】
Spring Cloud(一):服务治理技术概览[Finchley 版] 发表于 2018-04-14 | 更新于 2018-05-07 | Spring Cloud Netflix 是 Spr ...
- 廖雪峰git笔记
查看本地机子的在Git上的名字和邮箱:git config user.namegit config user.email 对所有仓库指定相同的用户名和Email地址:git config --glob ...
- 论文笔记:Fully-Convolutional Siamese Networks for Object Tracking
Fully-Convolutional Siamese Networks for Object Tracking 本文作者提出一个全卷积Siamese跟踪网络,该网络有两个分支,一个是上一帧的目标,一 ...
- springboot集成jpa,在postgresql数据库中创建主键自增表
依赖文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http:/ ...
- 第五章—if语句
5-1 条件测试 :编写一系列条件测试:将每个测试以及你对其结果的预测和实际结果都打印出来.你编写的代码应类似于下面这样: car = 'subaru' print("Is car == ' ...
.jpg)
