插头DP总结

 

特征:

一般为网格图

求解联通性问题或给出特定形状

可状压的数据范围

插头可定义,能够归纳轮廓线以上的情况且推出所以以下的情况,统计类要不充不漏(统计答案时判掉也行)

状态数不多

 

实现:

Hash表压缩状态

强烈建议使用函数,嵌套调用节约码长,不容易出错。

 1 int sc[5]={0,1,-1};
 2 struct Hash_Table{
 3     #define mod 131313
 4     int fir[mod+5],nxt[mod+5],key[mod+5],o;
 5     ll val[mod+5];
 6     void clear(){o=0;memset(fir,0,sizeof fir);}
 7     ll& operator[](int x){
 8         int pos=x%mod;
 9         for(reg int i=fir[pos];i;i=nxt[i])
10             if(key[i]==x)return val[i];
11         key[++o]=x;
12         nxt[o]=fir[pos];
13         fir[pos]=o;
14         return val[o]=0;
15     }
16 }f[2];
17 int find(int sta,int pos)
18 {
19     return sta>>(pos-1<<1)&3;
20 }
21 int ch(int sta,int pos,int val)
22 {
23     pos=(pos-1)<<1;
24     sta|=3<<pos;
25     sta^=3<<pos;
26     sta|=val<<pos;
27     return sta;
28 }
29 int link(int sta,int pos)
30 {
31     int dt=sc[find(sta,pos)],cnt=0;
32     for(reg int i=pos;i&&i<=m+1;i+=dt){
33         cnt+=sc[find(sta,i)];
34         if(cnt==0)return i;
35     }
36     return -1;
37 }
View Code

find(sta,pos)  返回sta中pos位的插头类型

ch(sta,pos,val)  更改sta中pos位的插头类型为val

link(sta,pos)  返回sta中pos位插头对应的另一端位置

 

调错(因为真的很难调qwq):

输出(i,j),状态,插头类型

模出样例形状,看在对应的位置有没有正确的状态。

link()函数返回-1可以便于反映bug。

RE一般是hash表开小或状态出负(以上)

 

注意:插头是已经发生的状态,但对于不好在当前判定合法性的状态,可以先出插头累加当前格而不算转移格的贡献,放在之后判定。

 

例题:以下重点在于定义插头

《Ural 1519 Formula 1》:一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数。

采用括号匹配压状态,定义1为左括号,2为右括号。

《CITY》:插头同理,与上题不同只在于转移条件。

《地板》:地板只能转向一次,于是定义插头1为未转向,2为已转向。

《标识设计》:三个L没有本质区别,体现在没有顺序、不可合并,所以定义单插头表示L的走向。但必须保证数量,所以记录状态中已开始和已结束的插头数量(<=3)。转移完统计开始和结束都为3的答案即可。可以不记录已结束数,最后判下没有插头就行。

《神奇游乐园》:不同在于:

1.在任何位置都可以闭合也可以无插头。加上对应的转移。

2.最优化问题。  改为取max

《ParkII》要求的是路径,所以只有括号插头不再适用

定义1 2为括号插头,3为独立插头

多出来的转移:

1.括号插头和独立插头合并,改另一端为独立插头

2.独立和独立插头合并,若无其他插头则统计答案,否则是不合法状态舍去。

3.p1=1&p2=2  括号合并,不合法

4.括号插头固定一端,另一端变独立插头

posted @ 2019-12-11 13:50  hzoi_yzh  阅读(246)  评论(0编辑  收藏  举报