博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
BZOJ2595:[Wc2008]游览计划——题解(插头dp)
阅读量:6212 次
发布时间:2019-06-21

本文共 4042 字,大约阅读时间需要 13 分钟。

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 0

Sample Output

6

xoox
___o
___o
xoox

 参(抄)考(了):

……原本也不想抄的啊,不然括号表达法还得学独立插头……

打眼一看就是一个插头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
#include
using namespace std;const int INF=2147483647;const int mod=999983;const int N=11;const int M=1000005;struct node{
//哈希表 int nxt; int state,ans,pos,pre;//状态,答案,编号,前一个状态 bool choose;//是否选择该方块 }edge[M];int head[mod+1],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
=1;i--){ w[i]=now&7; now>>=3; } return;}inline int encode(){ int x=0,tot=0; memset(mapp,-1,sizeof(mapp)); mapp[0]=0; for(int i=1;i<=m;i++){ if(mapp[w[i]]==-1)mapp[w[i]]=++tot; w[i]=mapp[w[i]]; x=x<<3|w[i]; } return x;}inline void init(){ memset(head,0,sizeof(head)); lcnt=rcnt+1;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=1; for(int l=1;l<=m&&flag;l++){ if(w[l]>1)flag=0; } if(num
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-1];//这个格子左面格子的右插头 int is_down=w[j];//这个格子上面格子的下插头 bool flag=0;//判断是否可以放插头,1可以不放 if(!mp[i][j])flag=0;//景点必须放 else if(!is_down)flag=1;//下插头不需要再匹配,故可以不放。 else{ flag=0; for(int l=1;l<=m&&!flag;l++){ if(l!=j&&w[l]==is_down)flag=1; //上插头已经与轮廓线的一端匹配,可以在那一端延伸。 } //没有匹配的插头,必须要接上 } if(flag){
//不放插头 w[j]=0;//下插头没有用了 insert(encode(),num,(i-1)*m+j,k,0); w[j]=is_down; } //放插头 if(!is_right&&!is_down)w[j]=7;//一个新插头 else if(!is_down&&is_right)w[j]=is_right;//右插头延续过来 else if(is_right!=is_down&&is_right&&is_down){ for(int l=1;l<=j;l++){
//不太好说,但画个图应该就理解了 if(w[l]==is_right)w[l]=is_down; } } //如果下插头和右插头匹配那么就把它们连起来 insert(encode(),num+mp[i][j],(i-1)*m+j,k,1); } } } init(); getans(); return;}void getans(int k){ if(!k)return; int r=edge[k].pos; int l=(r-1)/m+1;r=(r-1)%m+1; g[l][r]=edge[k].choose; getans(edge[k].pre); return;}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ for(int j=1;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=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(!mp[i][j])putchar('x'); else if(g[i][j])putchar('o'); else putchar('_'); } putchar('\n'); } return 0;}

 

转载于:https://www.cnblogs.com/luyouqi233/p/8258525.html

你可能感兴趣的文章
【BZOJ】3676: [Apio2014]回文串
查看>>
【LibreOJ】【LOJ】#6217. 扑克牌
查看>>
Greatest Number(山东2010省赛)
查看>>
EOJ Monthly 2018.1
查看>>
document.compatMode属性
查看>>
Servlet学习笔记
查看>>
CyclicBarrier的应用场景
查看>>
20172318 《程序设计与数据结构》第三周学习总结
查看>>
Windows下安装phpRedis扩展
查看>>
在Visual Studio中将现有.NET Framework项目迁移至.NET Core 1.1 Preview 1
查看>>
电子商城实录------载入数据库模型
查看>>
为什么在vue的组件中,data要用function返回对象呢?
查看>>
使用selenium模拟登陆点击登陆按钮
查看>>
ligerui tab 部分记载
查看>>
Service服务
查看>>
1060. Are They Equal (25)
查看>>
win10在当前目录下 打开cmd
查看>>
jquery.extend 与 jquery.fn.extend的区别和使用
查看>>
NFS存储服务器的部署流程
查看>>
计算机网络术语总结2
查看>>