插头\(DP\)学习小结

这种辣鸡毒瘤东西也能叫算法。。。

很优秀的一个算法。

最基本的适用范围主要是数据范围极小的网格图路径计数问题。

如果是像\(Noi2018\)那种的话建议考生在其他两道题难度超过普及组的情况下放弃这题。

其实大佬想做也可以去刚一下

切记如果在考场上看到这种题目,千万不要觉得你看出正解就是切了此题。

请一定将插头\(DP\)题当做一道毒瘤大模拟看待。

要点

这种东西细节挺多的,如果是比较灵活的题目那些转移一定都要好好考虑清楚,尽量做到一次过,否则调试时间可能会爆炸。

目前只做了一道题就是清华集训的简单回路。

用自己的写法加借鉴\(Zsy\)以及萝卜的实现思路,我完成了这道题。

下面是细节注意点:

\(1.\) 关于状态的设计。首先像这道题可以使用括号序列来优化状态,其他题目也可能有相应的方法来优化状态。那么,以这题打比方,我们采用括号序列来优化状态,比较常见的做法有几种状态表示。

一种是三进制写法,将每一个格子的状态压成三进制数(左括号,右括号,无括号)来进行转移,无用的状态在转移的过程中处理掉从而不影响复杂度,这样的实现比较直接,在转移时的做法在后面会提到。

第二种是四进制写法,与三进制类似,它比三进制优秀的一点是可以使用系统的位运算从而达到看起来更加优秀的常数以及更加顺手的实现。

以上两种对于空间都有所浪费,当然也可以通过\(Hash\)或者\(Map\)把有效状态压到一起,这点就根据题目的空间要求以及考场上时间的充裕程度而定了。

我用的方法和以上都有不一样的地方,我将有用的状态直接搜了出来存在\(vector\)里面,并加以编码,需要回映射的时候使用\(map\)来查询标号,因为对于\(map\)的调用都在预处理里面所以并不会对总复杂度产生影响,这样调值的时候就是直接访问数组了,在我看来这样比较方便。

而上面三种写法的状态都是从左到右记录轮廓线的状态,另外还有一种写法是把竖着的那一条轮廓线记在一个固定位置,其他的位置与网格图一一对应。

当然这些都只是个人习惯问题,在灵活处理的情况下是不影响代码正确性的。

\(2.\) 关于一般转移。一般是进行分类讨论,对当前格子的两条轮廓线是否有插头以及它是左括号还是右括号进行讨论,根据题目来画图辅助理解转移即可。实现方式可以预处理类似建图一样将有效转移连一条边,也可以是直接写一个转移函数在转移的过程中大力讨论,这里建议前一种,后一种可能导致转移的常数过大甚至影响到复杂度。

另外,对于每道题的转移都会有各自的一些要点,比如简单回路这题,我们需要的是单一的一条回路,所以对于两条轮廓线的状态分别是\(1,2\)(左括号,右括号)的时候,是不能转移到这两个括号合并的状态去的,因为这样的两个括号是一对括号,如果闭合就会在当前状态中形成一个独立的回路,从而产生不合法的贡献。

上面前三种写法在一般转移的时候是没有不同之处的,第四种可能会麻烦一点,具体可以问问萝卜第四种的写法。

\(3.\) 关于障碍物。对于障碍物的转移,我们需要额外新增一种转移,每到障碍物格子的时候,将该两条轮廓线上没插头的状态复制出去,其他转移都是不合法的,这里所有的写法应该都大同小异。

\(4.\) 关于边界时。当你\(DP\)到一行的最后一个需要跳到下一行时,你需要新增一种转移,并且在每次换行时调用。对于前三种实现方式,显然有效的状态是最后一条轮廓线无插头的状态,然后将竖的轮廓线(最后一条)移到第一位,其他整体向后移动一位。对于前两种转移运用位运算(三进制自己写)来比较方便地完成,而我的写法则利用\(vector\)的自带函数\(pop\_back\)以及\(insert\)来完成,也比较方便,同时因为是预处理所以并不影响复杂度(\(insert\)是\(O(n)\)的)。而对于第四种,我们发现它并不需要任何额外的转移,正常做即可,因为他的竖轮廓线不受位置的影响。

\(5.\) 关于两个括号序列是否可以合并。部分题目(比如这题)就需要判断两个括号序列是否可以合并而形成一个完整的回路(或者多个环之类的)。关于上面的问题有两种做法。

第一种是模拟走环,就是模拟在这个环上的行走过程,某一时刻从一个括号走出去了或者走到起点时有括号没走到则是不合法。

第二种用并查集。首先检查括号个数是否相同,然后将同一个括号的两个括号并起来,再上下依次相并,最后检查所有括号是否都被并到了一起。我用的是第二种,但是这种方法扩展性并不高,也许需要一定的改良,建议理解第一种。

\(6.\) 关于常数以及复杂度。再提一下,插头\(DP\)的效率很大程度上可以从预处理中优化,即可以通过预处理来对代码效率进行优化,有时可以直接降低复杂度,如果你被卡常了,尝试多预处理一些东西,特别是在复杂度最高的那些地方。

大概就是这一些吧,有什么后面再补充。这里贴一下简单回路的代码。(至少刚交的时候是\(UOJ\)的\(RK1\))

  1. #include<cstdio>
  2. #include<map>
  3. #include<vector>
  4. using namespace std;
  5. #define N 1010
  6. #define ll long long
  7. #define RG register
  8. #define MOD 1000000007
  9. inline int read(){
  10. RG int x=0,t=1;RG char ch=getchar();
  11. while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
  12. if(ch=='-')t=-1,ch=getchar();
  13. while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
  14. return x*t;
  15. }
  16. int n,m,K,Q,Map[N][10];
  17. vector<int> V[150]; int cnt; map<vector<int>,int> M;
  18. inline int Plu(const int &x,const int &y) { return x+y>=MOD?x+y-MOD:x+y; }
  19. inline void Dfs(int k,int L,vector<int> now){
  20. if(k==m+2){ if(!L) V[++cnt]=now,M[now]=cnt; return ; }
  21. now.push_back(0),Dfs(k+1,L,now),now.pop_back();
  22. now.push_back(1),Dfs(k+1,L+1,now),now.pop_back();
  23. if(L) now.push_back(2),Dfs(k+1,L-1,now),now.pop_back();
  24. }
  25. int f[2][150],F[N][150],G[N][150],Ans[N][10];
  26. struct mona { int nxt,en; } ;
  27. struct Trans{
  28. int first[150],top; mona s[150*150];
  29. inline void Insert(int x,int y) { s[++top]=(mona) { first[x],y },first[x]=top; }
  30. inline void To1(int j){
  31. vector<int> now;
  32. for(RG int i=1;i<=cnt;++i){
  33. int A=V[i][j-1],B=V[i][j];
  34. if(A==0&&B==0) now=V[i],now[j-1]=1,now[j]=2,Insert(i,M[now]),Insert(i,i);
  35. else if(A!=0&&B==0) now=V[i],now[j]=A,now[j-1]=0,Insert(i,M[now]),Insert(i,i);
  36. else if(A==0&&B!=0) now=V[i],now[j-1]=B,now[j]=0,Insert(i,M[now]),Insert(i,i);
  37. else if(A==2&&B==1) now=V[i],now[j-1]=now[j]=0,Insert(i,M[now]);
  38. else if(A==1&&B==1){ int t=1,k;
  39. for(k=j+1;t;++k)
  40. if(V[i][k]==1) ++t;
  41. else if(V[i][k]) --t;
  42. now=V[i],now[k-1]=1,now[j-1]=now[j]=0,Insert(i,M[now]);
  43. } else if(A==2&&B==2){ int t=1,k;
  44. for(k=j-2;t;--k)
  45. if(V[i][k]==1) --t;
  46. else if(V[i][k]) ++t;
  47. now=V[i],now[k+1]=2,now[j-1]=now[j]=0,Insert(i,M[now]);
  48. }
  49. }
  50. }
  51. inline void To2(){
  52. for(RG int i=1;i<=cnt;++i){
  53. if(V[i][m]) continue ; vector<int> now=V[i];
  54. now.pop_back(),now.insert(now.begin(),0);
  55. Insert(i,M[now]);
  56. } return ;
  57. }
  58. inline void To3(int j){
  59. for(RG int i=1;i<=cnt;++i)
  60. if(!V[i][j-1]&&!V[i][j]) Insert(i,i);
  61. }
  62. inline void Gone(int *F,int *G){
  63. for(RG int i=1;i<=cnt;++i)
  64. for(RG int j=first[i];j;j=s[j].nxt)
  65. G[s[j].en]=Plu(G[s[j].en],F[i]);
  66. }
  67. } A[10],C[10],D;
  68. struct Answer { int a,b; } st[150*150]; int tot,fa[20],tmp1[10],tmp2[10];
  69. inline int Find(int x) { if(fa[x]!=x) fa[x]=Find(fa[x]); return fa[x]; }
  70. inline void Merge(int x,int y) { int fx=Find(x),fy=Find(y); if(fx^fy) fa[fx]=fy; }
  71. inline bool Check(int A,int B){
  72. int tt1=0,tt2=0,cp=0; if(V[A][m]) return 0;
  73. for(RG int i=1;i<=m;++i){
  74. if((V[A][i]&&!V[B][i])||(!V[A][i]&&V[B][i])) return 0;
  75. fa[i]=i,fa[i+m]=i+m;
  76. }
  77. for(RG int i=0;i^m;++i){
  78. if(V[A][i]==1) tmp1[++tt1]=i+1; if(V[A][i]==2) Merge(tmp1[tt1--],i+1);
  79. if(V[B][i]==1) tmp2[++tt2]=i+1+m; if(V[B][i]==2) Merge(tmp2[tt2--],i+1+m);
  80. } for(RG int i=0;i^m;++i) if(V[A][i]&&V[B][i]) Merge(i+1,i+1+m);
  81. for(RG int i=0;i^m;++i){
  82. if(!V[A][i]) continue ; if(!cp) cp=Find(i+1);
  83. if(Find(i+1)^cp) return 0;
  84. }
  85. for(RG int i=0;i^m;++i){
  86. if(!V[B][i]) continue ; if(!cp) cp=Find(i+1+m);
  87. if(Find(i+1+m)^cp) return 0;
  88. } return 1;
  89. }
  90. inline void Pre(){
  91. for(RG int i=1;i<=cnt;++i)
  92. for(RG int j=1;j<=cnt;++j)
  93. if(Check(i,j)) st[++tot]=(Answer) { i,j };
  94. }
  95. int main(){
  96. freopen("travel.in","r",stdin);
  97. freopen("travel.out","w",stdout);
  98. n=read(),m=read(),K=read(),Dfs(1,0,V[0]);
  99. for(RG int i=1;i<=K;++i) Map[read()][read()]=1;
  100. for(RG int i=1;i<=m;++i) A[i].To1(i); D.To2(),Pre();
  101. for(RG int i=1;i<=m;++i) C[i].To3(i);
  102. int now=1,lst=0; f[lst][1]=1;
  103. for(RG int i=1;i<=n;++i){
  104. for(RG int k=1;k<=cnt;++k) f[now][k]=0;
  105. D.Gone(f[lst],f[now]),now^=lst^=now^=lst;
  106. for(RG int j=1;j<=m;++j,now^=lst^=now^=lst){
  107. for(RG int k=1;k<=cnt;++k) f[now][k]=0;
  108. if(!Map[i][j]) A[j].Gone(f[lst],f[now]);
  109. else C[j].Gone(f[lst],f[now]);
  110. } for(RG int k=1;k<=cnt;++k) F[i][k]=f[lst][k];
  111. } for(RG int k=1;k<=cnt;++k) f[lst][k]=0; f[lst][1]=1;
  112. for(RG int i=n;i;--i){
  113. for(RG int k=1;k<=cnt;++k) f[now][k]=0;
  114. D.Gone(f[lst],f[now]),now^=lst^=now^=lst;
  115. for(RG int j=1;j<=m;++j,now^=lst^=now^=lst){
  116. for(RG int k=1;k<=cnt;++k) f[now][k]=0;
  117. if(!Map[i][j]) A[j].Gone(f[lst],f[now]);
  118. else C[j].Gone(f[lst],f[now]);
  119. } for(RG int k=1;k<=cnt;++k) G[i][k]=f[lst][k];
  120. }
  121. for(RG int i=1;i^n;++i)
  122. for(RG int j=1;j<=m;++j)
  123. for(RG int k=1;k<=tot;++k){
  124. int p=st[k].a,q=st[k].b;
  125. if(V[p][j-1]&&V[q][j-1])
  126. Ans[i][j]=(1LL*F[i][p]*G[i+1][q]+Ans[i][j])%MOD;
  127. } Q=read(); while(Q--) printf("%d\n",Ans[read()][read()]);
  128. }

插头$DP$学习小结的更多相关文章

  1. flex学习小结

    接触到flex一个多月了,今天做一个学习小结.如果有知识错误或者意见不同的地方.欢迎交流指教. 画外音:先说一下,我是怎么接触到flex布局的.对于正在学习的童鞋们,我建议大家没事可以逛逛网站,看看人 ...

  2. Python 学习小结

    python 学习小结 python 简明教程 1.python 文件 #!/etc/bin/python #coding=utf-8 2.main()函数 if __name__ == '__mai ...

  3. react学习小结(生命周期- 实例化时期 - 存在期- 销毁时期)

    react学习小结   本文是我学习react的阶段性小结,如果看官你是react资深玩家,那么还请就此打住移步他处,如果你想给一些建议和指导,那么还请轻拍~ 目前团队内对react的使用非常普遍,之 ...

  4. objective-c基础教程——学习小结

    objective-c基础教程——学习小结   提纲: 简介 与C语言相比要注意的地方 objective-c高级特性 开发工具介绍(cocoa 工具包的功能,框架,源文件组织:XCode使用介绍) ...

  5. pthread多线程编程的学习小结

    pthread多线程编程的学习小结  pthread 同步3种方法: 1 mutex 2 条件变量 3 读写锁:支持多个线程同时读,或者一个线程写     程序员必上的开发者服务平台 —— DevSt ...

  6. ExtJs学习笔记之学习小结LoginDemo

    ExtJs学习小结LoginDemo 1.示例:(登录界面) <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  7. 点滴的积累---J2SE学习小结

    点滴的积累---J2SE学习小结 什么是J2SE J2SE就是Java2的标准版,主要用于桌面应用软件的编程:包括那些构成Java语言核心的类.比方:数据库连接.接口定义.输入/输出.网络编程. 学习 ...

  8. (转) Parameter estimation for text analysis 暨LDA学习小结

    Reading Note : Parameter estimation for text analysis 暨LDA学习小结 原文:http://www.xperseverance.net/blogs ...

  9. dubbo学习小结

    dubbo学习小结 参考: https://blog.csdn.net/paul_wei2008/article/details/19355681 https://blog.csdn.net/liwe ...

  10. 【转载】Hyperledger学习小结

    Hyperledger学习小结 自学Hyperledger Composer也有段时间了,是时候对所学的知识总结一下了.因为没有实际项目参与的话,差不多也就到此为止了.后续可能会去了解一下以太坊的技术 ...

随机推荐

  1. 大数据笔记(六)——HDFS的底层原理:JAVA动态代理和RPC

    一.Java的动态代理对象 实现代码如下: 1.接口类MyService package hdfs.proxy; public interface MyService { public void me ...

  2. SQL Server DACPAC数据库部署错误

    DACPAC使用sqlpackage.exe进行部署,部署时候报错: EXEC : error SQL72035: [dbo].[table] is under change data capture ...

  3. C++新旧类型转换小记

    旧式类型转换可应对一切转换,不管合不合理,有没有风险,你让我转我就转给你,后果自负. 新式类型转换比较安全,主要体现在父子类之间的运行时转换 dynamic_cast上,若转换失败则返回空指针,而旧式 ...

  4. 【ABAP系列】SAP ABAP 用BAPI批量导入物料的质量视图

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP ABAP 用BAPI批量导入 ...

  5. FileSystemObject详解

    FSO是FileSystemObject 或 Scripting.FileSystemObject 的缩写,为 IIS 内置组件,用于操作磁盘.文件夹或文本文件.FSO 的对象.方法和属性非常的多,这 ...

  6. chineseocr项目的配置阶段出现的问题及解决方案

    chineseocr为GitHub上的一个开源项目,主要使用yolos,crnn等深度学习框架训练好后的模型使用.测试结果发现,不管是针对文本文件.表格文件.还是场景图,如身份证火车票,识别效果都比较 ...

  7. 前端 CSS的选择器 伪元素选择器

    介绍常用的伪元素. after用得比较多的 first-letter 用于为文本的第一个首字母设置样式. <!DOCTYPE html> <html lang="en&qu ...

  8. Java第三周总结&实验报告(1)

    总结:不知不觉,到了第三周,回顾这一周,我更加深入了解了main方法,除此之外,学习了两个关键字,一个this,一个static,this在强调属性时,只能放在句首且不能循环调用,static声明用于 ...

  9. 剑指Offer编程题(Java实现)——数组中的重复数字

    题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...

  10. 初探LINUX之--基础知识篇

    一 Linux哲学思想 1 一切都是一个文件(包含硬件) 2 小型,单一用途的程序 3 链接程序,共同完成复杂的任务 4 避免令人困惑的用户界面 5 配置数据存储在文本中 二 Linux重要概念 Sh ...