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 斯坦纳 ...
随机推荐
- MYSQL查看当前正在使用的数据库命令
select database();
- 【转】CentOS Linux解决Device eth0 does not seem to be present(linux)
原文来自:http://www.linuxidc.com/Linux/2012-12/76248.htm 在VMware里克隆出来的CentOS Linux.. ifconfig...没有看到eth0 ...
- 「日常训练」Magic Stones(CodeForces-1110E)
题意 给定两个数组c和t,可以对c数组中的任何元素变换\(c_i\)成\(c_{i+1}+c_{i-1}-c_i\),问c数组在若干次变换后能否变换成t数组. 分析 这种魔法题目我是同样的没做过. ...
- vue中的样式
一.使用class样式: CSS部分: <style> .green{ color:green; } .italic{ font-style:italic; } .thin{ ; } .a ...
- spring-boot 项目整合logback
使用spring-boot项目中添加日志输出,java的日志输出一共有两个大的方案log4j/log4j2 ,logback.log4j2算是对log4j的一个升级版本. 常规做法是引入slf4j作为 ...
- Dijkstra 最短路径算法 秒懂详解
想必大家一定会Floyd了吧,Floyd只要暴力的三个for就可以出来,代码好背,也好理解,但缺点就是时间复杂度高是O(n³). 于是今天就给大家带来一种时间复杂度是O(n²),的算法:Dijkstr ...
- Linux发行版本应用场景
如果你是一个Linux爱好者,想选择一个桌面系统,并且既不想用盗版,又不想花太多钱购买商业系统软件,那么可以选择Ubuntu桌面系统.如果你需要服务器端的Linux系统,想用一个比较稳定的服务器系统, ...
- 作业三C++
作业心得 1.本次作业开始使用C++编写了(面向过程的C++,2333) 2.粗略学习了一下文件输入输出,和项目的创建等(在大佬眼里最基本的操作QAQ,然而我还是有点晕晕的,平时都是ctrl+n新建源 ...
- a6
组员:陈锦谋 今日内容: 界面按钮.icon制作,PS学习 明日计划: 继续 困难: 时间不多吧,今天主要电气实践
- HDU 5179 beautiful number 数位dp
题目链接: hdu: http://acm.hdu.edu.cn/showproblem.php?pid=5179 bc(中文): http://bestcoder.hdu.edu.cn/contes ...