基于连通性的状压dp

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

就是讨论麻烦一些。

简介

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

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

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

状态存储方法:

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

哈希+滚动数组

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

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

例题

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

经典入门例题:HDU 1693

Eat the Trees

代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=;
  5. int n,m;
  6. int mp[N+][N+];
  7. int T;
  8. ll f[N+][N+][<<(N+)];
  9. ll ans;
  10. void wrk(){
  11. for(int i=;i<=n;i++){
  12. for(int j=;j<=m;j++){
  13. for(int s=;s<(<<m+);s++){
  14. if(f[i][j][s]==) continue;
  15.  
  16. if(j!=m){// not a end
  17. //cout<<" here "<<i<<" "<<j<<" "<<s<<endl;
  18. int le=(s>>j-)&;
  19. int up=(s>>j)&;
  20. if(mp[i][j]==){
  21. if(le==&&up==){
  22. f[i][j+][s]+=f[i][j][s];
  23. }
  24. }
  25. else{
  26. int now=s;
  27. if(le==&&up==){
  28. int s0=s^(<<j-);
  29. s0|=(<<j);
  30. f[i][j+][s0]+=f[i][j][s];
  31. f[i][j+][s]+=f[i][j][s];
  32. }
  33. else if(le==&&up==){
  34. int s0=s^(<<j-);
  35. s0^=(<<j);
  36. f[i][j+][s0]+=f[i][j][s];
  37. }
  38. else if(le==&&up==){
  39. int s0=s^(<<j);
  40. s0|=(<<j-);
  41. f[i][j+][s0]+=f[i][j][s];
  42. f[i][j+][s]+=f[i][j][s];
  43. }
  44. else{
  45. int s0=s|(<<j);
  46. s0|=(<<j-);
  47. f[i][j+][s0]+=f[i][j][s];
  48. }
  49. }
  50. }
  51. else{// j==m
  52. int le=(s>>j-)&;
  53. int up=(s>>j)&;
  54. if(mp[i][j]==){
  55. if(le==&&up==){
  56. f[i+][][(s<<)]+=f[i][j][s];
  57. }
  58. }
  59. else{
  60. int now=s;
  61. //cout<<" irhf "<<i<<" "<<j<<" : "<<s<<" "<<le<<" "<<up<<endl;
  62. if(le==&&up==){
  63.  
  64. f[i+][][s<<]+=f[i][j][s];
  65. }
  66. else if(le==&&up==){
  67. int s0=s^(<<j-);
  68. s0^=(<<j);
  69. f[i+][][s0<<]+=f[i][j][s];
  70. }
  71. else if(le==&&up==){
  72. int s0=s^(<<j);
  73. s0|=(<<j-);
  74. f[i+][][s0<<]+=f[i][j][s];
  75. }
  76. else{
  77. continue;
  78. }
  79. }
  80. }
  81. }
  82. }
  83. }
  84. }
  85. int main()
  86. {
  87. scanf("%d",&T);
  88. for(int o=;o<=T;o++){
  89. scanf("%d%d",&n,&m);
  90. for(int i=;i<=n;i++){
  91. for(int j=;j<=m;j++){
  92. scanf("%d",&mp[i][j]);
  93. }
  94. }
  95. ans=;
  96. memset(f,,sizeof f);
  97. f[][][]=;
  98. wrk();
  99. /*for(int i=1;i<=n;i++)
  100. for(int j=1;j<=m;j++){
  101. cout<<i<<" and "<<j<<" : "<<endl;
  102. for(int s=0;s<=(1<<m+1);s++){
  103. cout<<" "<<s<<" |: "<<f[i][j][s]<<endl;
  104. }
  105. }*/
  106.  
  107. ans=f[n+][][];
  108. printf("Case %d: There are %lld ways to eat the trees.\n",o,ans);
  109. }
  110. return ;
  111. }

Eat the Trees

【模板】插头dp

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

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

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

  1. // luogu-judger-enable-o2
  2. // luogu-judger-enable-o2
  3. #include<bits/stdc++.h>
  4. #define il inline
  5. #define reg register int
  6. #define numb (ch^'0')
  7. using namespace std;
  8. typedef long long ll;
  9. il void rd(int &x){
  10. char ch;bool fl=false;
  11. while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
  12. for(x=numb;isdigit(ch=getchar());x=x*+numb);
  13. (fl==true)&&(x=-x);
  14. }
  15. namespace Miracle{
  16. const int N=;
  17. const int M=;
  18. const int mo=<<;
  19. int n,m;
  20. char mp[N][N];
  21. int bit[N];
  22. int lasx,lasy;
  23. int num(int s,int k){//s's kth pos
  24. return (s>>bit[k-])&;
  25. }
  26. void zip(int s){
  27. for(reg i=;i<=m+;++i){
  28. cout<<num(s,i)<<" ";
  29. }cout<<endl;
  30. }
  31. struct hah{
  32. int val[M],hd[mo],nxt[M];
  33. ll dp[M];
  34. int cnt;
  35. void upda(int s,ll v){
  36. int p=s%mo;
  37. // cout<<" upda ";zip(s);
  38. // cout<<"hash "<<s<<" v "<<v<<" p "<<p<<" cnt "<<cnt<<" hd "<<hd[p]<<endl;
  39. for(reg i=hd[p];i;i=nxt[i]){
  40. // cout<<"i "<<i<<" nxt "<<nxt[i]<<endl;
  41. if(val[i]==s){
  42. // cout<<" fin "<<i<<endl;
  43. dp[i]+=v;return;
  44. }
  45. }
  46. // cout<<"build new "<<endl;
  47. ++cnt;nxt[cnt]=hd[p];dp[cnt]=v;val[cnt]=s;
  48. hd[p]=cnt;
  49. }
  50. void clear(){
  51. memset(hd,,sizeof hd);cnt=;
  52. }
  53. }f[];
  54.  
  55. int ad(int k,int c){
  56. // cout<<" k "<<k<<" "<<c<<" bit "<<bit[k-1]<<endl;
  57. return (<<(bit[k-]))*c;
  58. }
  59. int get(int s,int k){
  60. int lp=num(s,k);
  61. if(lp==){//after
  62. int cnt=;
  63. for(reg i=k+;i<=m+;++i){
  64. if(num(s,i)==) ++cnt;
  65. else if(num(s,i)==) --cnt;
  66. if(cnt<) return i;
  67. }
  68. }else{//before
  69. int cnt=;
  70. for(reg i=k-;i>=;--i){
  71. if(num(s,i)==) --cnt;
  72. else if(num(s,i)==) ++cnt;
  73. if(cnt<) return i;
  74. }
  75. }
  76. cout<<" bug "<<endl;
  77. return -;//bug
  78. }
  79.  
  80. ll wrk(){
  81. int tmp=;
  82. f[tmp].upda(,);
  83. ll lim=(<<(*(m+)))-;
  84. for(reg i=;i<=n;++i){
  85. // cout<<" i "<<i<<endl;
  86. for(reg j=;j<=f[tmp].cnt;++j) {
  87. f[tmp].val[j]=(f[tmp].val[j]*)&lim;
  88. // cout<<" num "<<j<<" : "<<" dp "<<f[tmp].dp[j]<<" zip ";zip(f[tmp].val[j]);
  89. }
  90. for(reg j=;j<=m;++j){
  91. // cout<<" j "<<j<<endl;
  92. tmp^=;
  93. f[tmp].clear();
  94. int pr=tmp^;
  95. // cout<<" cnt "<<f[pr].cnt<<endl;
  96. for(reg k=;k<=f[pr].cnt;++k){
  97. // cout<<" k "<<k<<endl;
  98. int s=f[pr].val[k];
  99. // int u=num(s,j+1),l;//=num(s,j);
  100. ll v=f[pr].dp[k];
  101. // cout<<" v "<<v;cout<<" s ";zip(s);//endl;
  102. if(mp[i][j]=='*'){
  103. if(num(s,j)==&&num(s,j+)==) f[tmp].upda(s,v);
  104. }else{
  105. if(num(s,j)==&&num(s,j+)==){//new
  106. if(j!=m)f[tmp].upda(s+ad(j,)+ad(j+,),v);
  107. }
  108. else if(num(s,j)==){//only up
  109. // cout<<" only up"<<endl;
  110. f[tmp].upda(s+ad(j,num(s,j+))+ad(j+,-num(s,j+)),v);
  111. if(j!=m)f[tmp].upda(s,v);
  112. }else if(num(s,j+)==){//only left
  113. // cout<<" only left "<<endl;
  114. if(j!=m)f[tmp].upda(s+ad(j,-num(s,j))+ad(j+,num(s,j)),v);
  115. f[tmp].upda(s,v);
  116. // cout<<" after upda "<<endl;
  117. }else{//both
  118. // cout<<" both "<<endl;
  119. if(num(s,j)==&&num(s,j+)==){
  120. int to=s;to+=ad(j,-num(s,j))+ad(j+,-num(s,j+));
  121. to+=ad(get(s,j+),-);
  122. f[tmp].upda(to,v);
  123. }else if(num(s,j)==&&num(s,j+)==){
  124. int to=s;to+=ad(j,-num(s,j))+ad(j+,-num(s,j+));
  125. to+=ad(get(s,j),);
  126. f[tmp].upda(to,v);
  127. }else if(num(s,j)==&&num(s,j+)==){
  128. int to=s;to+=ad(j,-num(s,j))+ad(j+,-num(s,j+));
  129. f[tmp].upda(to,v);
  130. }else{
  131. if(i==lasx&&j==lasy){
  132. int to=s;to+=ad(j,-num(s,j))+ad(j+,-num(s,j+));
  133. f[tmp].upda(to,v);
  134. }
  135. }
  136. }
  137. }
  138. }
  139. }
  140. }
  141. for(reg i=;i<=f[tmp].cnt;++i){
  142. if(f[tmp].val[i]==) return f[tmp].dp[i];
  143. }
  144. return ;
  145. }
  146. int main(){
  147. rd(n);rd(m);
  148. for(reg i=;i<=;++i) bit[i]=i<<;
  149. for(reg i=;i<=n;++i){
  150. scanf("%s",mp[i]+);
  151. }
  152. for(reg i=;i<=n;++i){
  153. for(reg j=;j<=m;++j){
  154. if(mp[i][j]=='.') lasx=i,lasy=j;
  155. }
  156. }
  157. cout<<wrk();
  158. return ;
  159. }
  160.  
  161. }
  162. signed main(){
  163. Miracle::main();
  164. return ;
  165. }
  166.  
  167. /*
  168. Author: *Miracle*
  169. Date: 2019/3/31 21:44:47
  170. */

[学习笔记]插头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. VB6 CHECK is run as admin privilege

    vb6 code: Private Declare Function IsUserAnAdmin Lib "Shell32" Alias "#680" () A ...

  2. 20155234 昝昕明《基于ARM实验箱的国密算法应用》课程设计个人报告

    20155234 昝昕明<基于ARM实验箱的国密算法应用>课程设计个人报告 个人贡献 参与课设题目讨论及完成全过程: 资料收集: SM1算法及和ARM之间通信 负责串口代码调试: 协调完成 ...

  3. [Deep-Learning-with-Python]基于Keras的房价预测

    预测房价:回归问题 回归问题预测结果为连续值,而不是离散的类别. 波士顿房价数据集 通过20世纪70年代波士顿郊区房价数据集,预测平均房价:数据集的特征包括犯罪率.税率等信息.数据集只有506条记录, ...

  4. CS100.1x Introduction to Big Data with Apache Spark

    CS100.1x简介 这门课主要讲数据科学,也就是data science以及怎么用Apache Spark去分析大数据. Course Software Setup 这门课主要介绍如何编写和调试Py ...

  5. PowerBI开发 第十四篇:使用M公式添加列

    PowerBI的查询编辑器使用Power Query M公式语言来定义查询模型,它是一种富有表现力的数据糅合(Mashup)语言,一个M查询可以计算(Evalute)一个表达式,得到一个值. 对于开发 ...

  6. flask_admin 笔记三 客户化视图

    客户化视图1, model数据模型参数配置1)配置全局参数内置的ModelView类很适合快速入门. 但是,您需要配置其功能以适合您的特定型号. 这是通过设置ModelView类中提供的配置属性的值来 ...

  7. 推荐一个MacOS苹果电脑系统解压缩软件

    废话少说,直入主题: 连接:https://www.keka.io/en/ 开源免费好用(个人觉得比betterzip好用哈),附一张这货的图标:

  8. 290. Word Pattern【LeetCode by java】

    今天发现LintCode页面刷新不出来了,所以就转战LeetCode.还是像以前一样,做题顺序:难度从低到高,每天至少一题. Given a pattern and a string str, fin ...

  9. CodeMirror 小册子

    User manual and reference guide      version 5.41.1 用户手册和参考指南 CodeMirror is a code-editor component ...

  10. hive insert 动态分区异常(Error encountered near token)与解决

    当insert数据到有分区的hive表里时若不明显指定分区会抛出异常 insert overwrite table persons_tmp select * from persons; FAILED: ...