hdu1693 Eat the Trees [插头DP经典例题]
想当初,我听见大佬们谈起插头DP时,觉得插头DP是个神仙的东西。
某大佬:“考场见到插头DP,直接弃疗。”
现在,我终于懂了他们为什么这么说了。
因为——
插头DP很毒瘤!
为什么?
等等你就知道这是为什么了。
例题
hdu 1693 Eat the Trees
题目大意:给你一个地图,每个格子是空地或者障碍。现在要用若干个回路来覆盖这些空地,使得每个空地皆被一个回路覆盖。问方案数。
n,m<=11
How to solve it?
暴力?本人的暴力水平太弱了,对于这题,似乎连暴力都不好打。
So?咋做?
Of course,DP……
怎么划分阶段?
通常有逐行,逐列,逐格。你说的是什么鬼?
字面上的意思,逐行就是从上一行的状态转移到下一行,逐列类似。
逐格就是按照一定的顺序枚举每个格子(比如从上到下,从左到右),从上一个格子转移到下一个格子。
插头DP
插头是什么?
插头DP,最重要的当然是插头。
对于每一个格子,有上下左右四个插头,表示这个格子可以在这个方向与外面相连。
这题中,每个点的插头状况有下面七种。
其中,第0种情况是存在于障碍物的,剩下的6种存在于空地。
那么,我们在转移时,要接上轮廓线上边的插头,使其不会出现断头的情况。
轮廓线?就是你转移时候的一条线(没说一定是直线哈),这条线上面的每个格子都是你之前计算过的。
这幅图就是逐行转移的轮廓线。
做法
考虑逐行转移。
设fi,s" role="presentation">fi,sfi,s表示枚举到第i" role="presentation">ii行,轮廓线上的下插头状态为s" role="presentation">ss的方案数。
怎么转移?
既然这是插头DP,那么转移的时候,就要接上上面的插头。
仔细地考虑一下,转移状态太多,不好枚举,时间复杂度显然无法接受。
那我们就试一下逐格转移。
先放个图。
设fi,j,s" role="presentation">fi,j,sfi,j,s表示,枚举到i" role="presentation">ii行j" role="presentation">jj列,轮廓线上的插头状态为s" role="presentation">ss的方案数。
轮廓线上的插头显然是m" role="presentation">mm个下插头和1个右插头。
那么,转移?
既然要接上之前的插头,那么我们可以让之前的插头来接上它。
比如,1状态的格子的上方要有一个下插头,左边要有一个右插头。
2状态的格子上方要有一个下插头,左边不能有右插头。
那么我们枚举现在的状态,可以算出它从之前的那个状态推过来。
这个需要分类讨论……这就是插头DP毒瘤的原因。
还有一个比较毒瘤的是,最上面一行,最下面一行,以及最左边一列,需要特殊处理。
当然,如果你喜欢,最上面一行也不需特殊处理。
为什么要特殊处理?我觉得我不用说原因了。
代码
下面的代码,注释中例如no*yes
,表示上面没有下插头,左边有一个右插头。
话说存状态的话,听说处理连通性的插头DP要按顺序存,这里为图方便,就将右插头存到了最后。
/*Situation:
1:up left
2:down up
3:down left
4:down right
5:right left
6:right up
*/
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
int n,m;
int mat[11][11];
long long f[11][11][4096];
int main()
{
// freopen("in.txt","r",stdin);
// freopen("test.txt","w",stdout);
int T;
scanf("%d",&T);
for (int TT=1;TT<=T;++TT)
{
scanf("%d%d",&n,&m);
for (int i=0;i<n;++i)
for (int j=0;j<m;++j)
scanf("%d",&mat[i][j]);
memset(f,0,sizeof f);
if (mat[0][0])
f[0][0][1<<0|1<<m]=1;//4
else
f[0][0][0]=1;//Space
for (int j=1;j<m;++j)
if (mat[0][j])
{
for (int s_=0;s_<1<<j+1;++s_)
if (s_>>j&1)
{
int s=s_|1<<m;
f[0][j][s]+=f[0][j-1][s_^1<<j];//no*no->4
s=s_;
f[0][j][s]+=f[0][j-1][s_^1<<j|1<<m];//no*yes->3
}
else
{
int s=s_|1<<m;
f[0][j][s]+=f[0][j-1][s_|1<<m];//no*yes->5
}
}
else
{
for (int s=0;s<1<<j;++s)
f[0][j][s]+=f[0][j-1][s];
}
for (int i=1;i<n-1;++i)
{
if (mat[i][0])
{
for (int s_=0;s_<(1<<m);++s_)
if (s_&1)
{
int s=s_|1<<m;
f[i][0][s]+=f[i-1][m-1][s_^1];//no*no->4
s=s_;
f[i][0][s]+=f[i-1][m-1][s_];//yes*no->2
}
else
{
int s=s_|1<<m;
f[i][0][s]+=f[i-1][m-1][s_|1];//yes*no->6
}
}
else
{
for (int s=0;s<(1<<m);s+=2)
f[i][0][s]+=f[i-1][m-1][s];//no*no->Space
}
for (int j=1;j<m;++j)
if (mat[i][j])
{
for (int s_=0;s_<1<<m;++s_)
if (s_>>j&1)
{
int s=s_|1<<m;
f[i][j][s]+=f[i][j-1][s_^1<<j];//no*no->4
s=s_;
f[i][j][s]+=f[i][j-1][s_]+f[i][j-1][s_^1<<j|1<<m];//yes*no->2 no*yes->3
}
else
{
int s=s_|1<<m;
f[i][j][s]+=f[i][j-1][s_|1<<m]+f[i][j-1][s_|1<<j];//no*yes->5 yes*no->6
s=s_;
f[i][j][s]+=f[i][j-1][s_|1<<j|1<<m];//yes*yes->1
}
}
else
{
for (int s=0;s<1<<m;++s)
if (!(s>>j&1))
f[i][j][s]+=f[i][j-1][s];//no*no->Space
}
}
if (mat[n-1][0])
{
for (int s_=0;s_<(1<<m);++s_)
if (!(s_&1))
{
int s=s_|1<<m;
f[n-1][0][s]+=f[n-1-1][m-1][s_|1];//yes*no->6
}
}
else
{
for (int s=0;s<(1<<m);s+=2)
f[n-1][0][s]+=f[n-1-1][m-1][s];//no*no->Space
}
for (int j=1;j<m;++j)
if (mat[n-1][j])
{
for (int s_=0;s_<1<<m;++s_)
if (!(s_>>j&1))
{
int s=s_|1<<m;
f[n-1][j][s]+=f[n-1][j-1][s_|1<<m]+f[n-1][j-1][s_|1<<j];//no*yes->5 yes*no->6
s=s_;
f[n-1][j][s]+=f[n-1][j-1][s_|1<<j|1<<m];//yes*yes->1
}
}
else
{
for (int s=0;s<1<<m;++s)
if (!(s>>j&1))
f[n-1][j][s]+=f[n-1][j-1][s];//no*no->Space
}
long long ans=f[n-1][m-1][0];
printf("Case %d: There are %lld ways to eat the trees.\n",TT,ans);
}
return 0;
}
总结
插头DP的关键是,转移的时候要连上之前的插头,保证不要断掉。
打插头DP需要非常严谨,打错了就不妙了,还不好调。
某大佬说,这不是标准的插头DP,因为标准的插头DP还要维护连通性,好像是用什么括号序。
我才懒得管这么多,感性理解一下,插头DP就是有插头的DP吧……不过那些维护连通性的题,若有机会,我定会光顾一下。
这题作为例题,还是很好的。
hdu1693 Eat the Trees [插头DP经典例题]的更多相关文章
- HDU1693 Eat the Trees —— 插头DP
题目链接:https://vjudge.net/problem/HDU-1693 Eat the Trees Time Limit: 4000/2000 MS (Java/Others) Mem ...
- HDU1693 Eat the Trees 插头dp
原文链接http://www.cnblogs.com/zhouzhendong/p/8433484.html 题目传送门 - HDU1693 题意概括 多回路经过所有格子的方案数. 做法 最基础的插头 ...
- HDU 1693 Eat the Trees(插头DP)
题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...
- hdu 1693 Eat the Trees——插头DP
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1693 第一道插头 DP ! 直接用二进制数表示状态即可. #include<cstdio> # ...
- HDU 1693 Eat the Trees ——插头DP
[题目分析] 吃树. 直接插头DP,算是一道真正的入门题目. 0/1表示有没有插头 [代码] #include <cstdio> #include <cstring> #inc ...
- hdu1693:eat trees(插头dp)
题目大意: 题目背景竟然是dota!屠夫打到大后期就没用了,,只能去吃树! 给一个n*m的地图,有些格子是不可到达的,要把所有可到达的格子的树都吃完,并且要走回路,求方案数 题解: 这题大概是最简单的 ...
- hdu1693 Eat the Trees 【插头dp】
题目链接 hdu1693 题解 插头\(dp\) 特点:范围小,网格图,连通性 轮廓线:已决策点和未决策点的分界线 插头:存在于网格之间,表示着网格建的信息,此题中表示两个网格间是否连边 状态表示:当 ...
- [Hdu1693]Eat the Trees(插头DP)
Description 题意:在n*m(1<=N, M<=11 )的矩阵中,有些格子有树,没有树的格子不能到达,找一条或多条回路,吃完所有的树,求有多少种方法. Solution 插头DP ...
- 2019.01.23 hdu1693 Eat the Trees(轮廓线dp)
传送门 题意简述:给一个有障碍的网格图,问用若干个不相交的回路覆盖所有非障碍格子的方案数. 思路:轮廓线dpdpdp的模板题. 同样是讨论插头的情况,只不过没有前一道题复杂,不懂的看代码吧. 代码: ...
随机推荐
- C++ BASS 实例
#include <iostream> #include <string> #include <map> #include "..\sdk\bass\in ...
- spring Cache + Redis 开发数据字典以及自定义标签
一.数据库表结构 1. 分类表:dict_type 2. 子项表:dict_entry 二.页面维护功能示意图: 1. 分类管理 点击子项管理进入子项管理页面 2.子项管理 三.数据字典添加到缓 ...
- tornado接收ajax的post请求报错WARNING:tornado.access:405 OPTIONS /add
后端报错信息 WARNING:tornado.access:405 OPTIONS /add (::1) 1.00m 前端报错信息 2xhr.js?ec6c:172 OPTIONS http://lo ...
- CentOS 7.2部署NTP服务器实现时间同步
CentOS 7.2部署NTP服务器实现时间同步 [日期:2017-12-18] 来源:Linux社区 作者:梁明远 [字体:大 中 小] 1. 前言 对于容器编排系统,前段时间主要研究kube ...
- 手工编写JavaWeb项目
手工编写JavaWeb项目 一.打开Tomcat服务器 二.编写简单的web项目 三.访问项目 并且,tomcat服务器也是可以直接访问.txt的,其实就和其它的web服务器一样,什么都可以访问,和之 ...
- Activity详解三 启动activity并返回结果 转载 https://www.cnblogs.com/androidWuYou/p/5886991.html
首先看演示: 1 简介 .如果想在Activity中得到新打开Activity 关闭后返回的数据,需要使用系统提供的startActivityForResult(Intent intent, int ...
- Django rest framework (视图类详解)
官网:https://www.django-rest-framework.org/api-guide/viewsets/ 在django rest framework 视图中一共有N个类 第一类:AP ...
- openstack各组件介绍
Nova:计算服务,通过虚拟化技术,实现虚拟机的创建,管理,删除,是openstack中最核心的服务. Neutron:网络服务,为虚拟机提供网络连接服务,就像物理机的交换机和路由器一样 Glance ...
- Git初次使用,记录自己看
Git官网下载:https://git-scm.com/downloads 官网如果太慢,可以去这下载:http://www.wmzhe.com/soft-38801.html,注意选择如下图地址下载 ...
- AngularJs 报错 Error: [$parse:lexerr]
参考:https://www.cnblogs.com/fangshidaima/p/6048071.html 错误: 根据错误找到报错行: $scope.$apply($scope.param1 = ...