基于连通性的状压dp

巧妙之处:插头已经可以表示内部所有状态了。

就是讨论麻烦一些。

简介

转移方法:
逐格转移,分类讨论

记录状态方法:
最小表示法(每次要重新编号,对于一类没用“回路路径”之类的题,可以胜任)

括号表示法(便于操作,但是一些题不能记录状态)

状态存储方法:

不能直接循环所有可能状态,因为状态不满太浪费

哈希+滚动数组

(clear时候,直接memset(hd),cnt=0就是最快的!!!!)

然后具体题目分清楚转移情况讨论即可。

例题

尝试加入各种剪枝以减少状态量。

经典入门例题:HDU 1693

Eat the Trees

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=;
int n,m;
int mp[N+][N+];
int T;
ll f[N+][N+][<<(N+)];
ll ans;
void wrk(){
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
for(int s=;s<(<<m+);s++){
if(f[i][j][s]==) continue; if(j!=m){// not a end
//cout<<" here "<<i<<" "<<j<<" "<<s<<endl;
int le=(s>>j-)&;
int up=(s>>j)&;
if(mp[i][j]==){
if(le==&&up==){
f[i][j+][s]+=f[i][j][s];
}
}
else{
int now=s;
if(le==&&up==){
int s0=s^(<<j-);
s0|=(<<j);
f[i][j+][s0]+=f[i][j][s];
f[i][j+][s]+=f[i][j][s];
}
else if(le==&&up==){
int s0=s^(<<j-);
s0^=(<<j);
f[i][j+][s0]+=f[i][j][s];
}
else if(le==&&up==){
int s0=s^(<<j);
s0|=(<<j-);
f[i][j+][s0]+=f[i][j][s];
f[i][j+][s]+=f[i][j][s];
}
else{
int s0=s|(<<j);
s0|=(<<j-);
f[i][j+][s0]+=f[i][j][s];
}
}
}
else{// j==m
int le=(s>>j-)&;
int up=(s>>j)&;
if(mp[i][j]==){
if(le==&&up==){
f[i+][][(s<<)]+=f[i][j][s];
}
}
else{
int now=s;
//cout<<" irhf "<<i<<" "<<j<<" : "<<s<<" "<<le<<" "<<up<<endl;
if(le==&&up==){ f[i+][][s<<]+=f[i][j][s];
}
else if(le==&&up==){
int s0=s^(<<j-);
s0^=(<<j);
f[i+][][s0<<]+=f[i][j][s];
}
else if(le==&&up==){
int s0=s^(<<j);
s0|=(<<j-);
f[i+][][s0<<]+=f[i][j][s];
}
else{
continue;
}
}
}
}
}
}
}
int main()
{
scanf("%d",&T);
for(int o=;o<=T;o++){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
scanf("%d",&mp[i][j]);
}
}
ans=;
memset(f,,sizeof f);
f[][][]=;
wrk();
/*for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cout<<i<<" and "<<j<<" : "<<endl;
for(int s=0;s<=(1<<m+1);s++){
cout<<" "<<s<<" |: "<<f[i][j][s]<<endl;
}
}*/ ans=f[n+][][];
printf("Case %d: There are %lld ways to eat the trees.\n",o,ans);
}
return ;
}

Eat the Trees

【模板】插头dp

https://www.luogu.org/problemnew/show/P5056

直接memset就是最好用的了。。。非要暴力清空结果TLE了一夜一页

对于下面和右面有障碍的,就不要填出插头了。剪枝

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=;
const int M=;
const int mo=<<;
int n,m;
char mp[N][N];
int bit[N];
int lasx,lasy;
int num(int s,int k){//s's kth pos
return (s>>bit[k-])&;
}
void zip(int s){
for(reg i=;i<=m+;++i){
cout<<num(s,i)<<" ";
}cout<<endl;
}
struct hah{
int val[M],hd[mo],nxt[M];
ll dp[M];
int cnt;
void upda(int s,ll v){
int p=s%mo;
// cout<<" upda ";zip(s);
// cout<<"hash "<<s<<" v "<<v<<" p "<<p<<" cnt "<<cnt<<" hd "<<hd[p]<<endl;
for(reg i=hd[p];i;i=nxt[i]){
// cout<<"i "<<i<<" nxt "<<nxt[i]<<endl;
if(val[i]==s){
// cout<<" fin "<<i<<endl;
dp[i]+=v;return;
}
}
// cout<<"build new "<<endl;
++cnt;nxt[cnt]=hd[p];dp[cnt]=v;val[cnt]=s;
hd[p]=cnt;
}
void clear(){
memset(hd,,sizeof hd);cnt=;
}
}f[]; int ad(int k,int c){
// cout<<" k "<<k<<" "<<c<<" bit "<<bit[k-1]<<endl;
return (<<(bit[k-]))*c;
}
int get(int s,int k){
int lp=num(s,k);
if(lp==){//after
int cnt=;
for(reg i=k+;i<=m+;++i){
if(num(s,i)==) ++cnt;
else if(num(s,i)==) --cnt;
if(cnt<) return i;
}
}else{//before
int cnt=;
for(reg i=k-;i>=;--i){
if(num(s,i)==) --cnt;
else if(num(s,i)==) ++cnt;
if(cnt<) return i;
}
}
cout<<" bug "<<endl;
return -;//bug
} ll wrk(){
int tmp=;
f[tmp].upda(,);
ll lim=(<<(*(m+)))-;
for(reg i=;i<=n;++i){
// cout<<" i "<<i<<endl;
for(reg j=;j<=f[tmp].cnt;++j) {
f[tmp].val[j]=(f[tmp].val[j]*)&lim;
// cout<<" num "<<j<<" : "<<" dp "<<f[tmp].dp[j]<<" zip ";zip(f[tmp].val[j]);
}
for(reg j=;j<=m;++j){
// cout<<" j "<<j<<endl;
tmp^=;
f[tmp].clear();
int pr=tmp^;
// cout<<" cnt "<<f[pr].cnt<<endl;
for(reg k=;k<=f[pr].cnt;++k){
// cout<<" k "<<k<<endl;
int s=f[pr].val[k];
// int u=num(s,j+1),l;//=num(s,j);
ll v=f[pr].dp[k];
// cout<<" v "<<v;cout<<" s ";zip(s);//endl;
if(mp[i][j]=='*'){
if(num(s,j)==&&num(s,j+)==) f[tmp].upda(s,v);
}else{
if(num(s,j)==&&num(s,j+)==){//new
if(j!=m)f[tmp].upda(s+ad(j,)+ad(j+,),v);
}
else if(num(s,j)==){//only up
// cout<<" only up"<<endl;
f[tmp].upda(s+ad(j,num(s,j+))+ad(j+,-num(s,j+)),v);
if(j!=m)f[tmp].upda(s,v);
}else if(num(s,j+)==){//only left
// cout<<" only left "<<endl;
if(j!=m)f[tmp].upda(s+ad(j,-num(s,j))+ad(j+,num(s,j)),v);
f[tmp].upda(s,v);
// cout<<" after upda "<<endl;
}else{//both
// cout<<" both "<<endl;
if(num(s,j)==&&num(s,j+)==){
int to=s;to+=ad(j,-num(s,j))+ad(j+,-num(s,j+));
to+=ad(get(s,j+),-);
f[tmp].upda(to,v);
}else if(num(s,j)==&&num(s,j+)==){
int to=s;to+=ad(j,-num(s,j))+ad(j+,-num(s,j+));
to+=ad(get(s,j),);
f[tmp].upda(to,v);
}else if(num(s,j)==&&num(s,j+)==){
int to=s;to+=ad(j,-num(s,j))+ad(j+,-num(s,j+));
f[tmp].upda(to,v);
}else{
if(i==lasx&&j==lasy){
int to=s;to+=ad(j,-num(s,j))+ad(j+,-num(s,j+));
f[tmp].upda(to,v);
}
}
}
}
}
}
}
for(reg i=;i<=f[tmp].cnt;++i){
if(f[tmp].val[i]==) return f[tmp].dp[i];
}
return ;
}
int main(){
rd(n);rd(m);
for(reg i=;i<=;++i) bit[i]=i<<;
for(reg i=;i<=n;++i){
scanf("%s",mp[i]+);
}
for(reg i=;i<=n;++i){
for(reg j=;j<=m;++j){
if(mp[i][j]=='.') lasx=i,lasy=j;
}
}
cout<<wrk();
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2019/3/31 21:44:47
*/

[学习笔记]插头dp的更多相关文章

  1. [学习笔记] 数位DP的dfs写法

    跟着洛谷日报走,算法习题全都有! 嗯,没错,这次我也是看了洛谷日报的第84期才学会这种算法的,也感谢Mathison大佬,素不相识,却写了一长篇文章来帮助我学习这个算法. 算法思路: 感觉dfs版的数 ...

  2. [学习笔记]区间dp

    区间 \(dp\) 1.[HAOI2008]玩具取名 \(f[l][r][W/I/N/G]\) 表示区间 \([l,r]\) 中能否压缩成 \(W/I/N/G\) \(Code\ Below:\) # ...

  3. [学习笔记]树形dp

    最近几天学了一下树形\(dp\) 其实早就学过了 来提高一下打开树形\(dp\)的姿势. 1.没有上司的晚会 我的人生第一道树形\(dp\),其实就是两种情况: \(dp[i][1]\)表示第i个人来 ...

  4. 【学习笔记】dp基础

    知识储备:dp入门. 好了,完成了dp入门,我们可以做一些稍微不是那么裸的题了. dp基础,主要是做题,只有练习才能彻底掌握. 洛谷P1417 烹调方案 分析:由于时间的先后会对结果有影响,所以c[i ...

  5. 【学习笔记】dp入门

    知识点 动态规划(简称dp),可以说是各种程序设计中遇到的第一个坎吧,这篇博文是我对dp的一点点理解,希望可以帮助更多人dp入门.   先看看这段话 动态规划(dynamic programming) ...

  6. [学习笔记]动态dp

    其实就过了模板. 感觉就是带修改的dp [模板]动态dp 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y表示修改点x的权值为y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小 ...

  7. [学习笔记]整体DP

    问题: 有一些问题,通常见于二维的DP,另一维记录当前x的信息,但是这一维过大无法开下,O(nm)也无法通过. 但是如果发现,对于x,在第二维的一些区间内,取值都是相同的,并且这样的区间是有限个,就可 ...

  8. [BZOJ4011][HNOI2015] 落忆枫音(学习笔记) - 拓扑+DP

    其实就是贴一下防止自己忘了,毕竟看了题解才做出来 Orz PoPoQQQ 原文链接 Description 背景太长了 给定一个DAG,和一对点(x, y), 在DAG中由x到y连一条有向边,求生成树 ...

  9. POJ3254Corn Fields (状态压缩or插头DP)

    Description Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; ...

随机推荐

  1. Python+Selenium爬取动态加载页面(1)

    注: 最近有一小任务,需要收集水质和水雨信息,找了两个网站:国家地表水水质自动监测实时数据发布系统和全国水雨情网.由于这两个网站的数据都是动态加载出来的,所以我用了Selenium来完成我的数据获取. ...

  2. WPF编程,窗体最大化、最小化、关闭按钮功能的禁用

    原文:WPF编程,窗体最大化.最小化.关闭按钮功能的禁用 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/detail ...

  3. Kafka查看topic、consumer group状态命令

    最近工作中遇到需要使用kafka的场景,测试消费程序启动后,要莫名的过几十秒乃至几分钟才能成功获取到到topic的partition和offset,而后开始消费数据,于是学习了一下查看kafka br ...

  4. C# 基于泛型的自定义线性节点链表集合示例

    本例子实现了如何自定义线性节点集合,具体代码如下: using System; using System.Collections; using System.Collections.Generic; ...

  5. vue JointJS 实例demo

    前言 越来越发现,前端深入好难哦!虐成渣渣了. 需求:前端绘制灵活的关系图(此demo还是简单的,我的需求才跨出一小步) 安装 npm install jointjs 容器,工具栏 <templ ...

  6. centos7 lldb 调试netcore应用的内存泄漏和死循环示例(dump文件调试)

    写个demo来玩一玩linux平台下使用lldb加载sos来调试netcore应用. 当然,在真实的产线环境中需要分析的数据和难度远远高于demo所示,所以demo的作用也仅仅只能起到介绍工具的作用. ...

  7. unity中camera摄像头控制详解

    目录 1. 缘起 2. 开发 2.1. 建立项目 2.2. 旋转 2.2.1. 四元数 2.3. 移动 2.3.1. 向量操作 2.4. 镜头拉伸 2.5. 复位 2.6. 优化 1 缘起 我们的产品 ...

  8. mgo like的两种写法

    实际上都是围绕正则来写的,看大家喜欢那种写法 package main import ( "fmt" "labix.org/v2/mgo" "labi ...

  9. Beta版本冲刺(三)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  10. Git 笔记——如何处理分支合并冲突

    1.前言 学习使用 Git 也有一段时间,但一直都是把 Git 当作一个代码仓库,使用的命令无非就是 clone, add, commit ,往往课程作业也没有过多人合作开发,没有体验过 Git 的分 ...