1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<vector> 5 #define mod 192617 6 #define ll long long 7 using namespace std; 8 int n,m; 9 int now,to;//滚动 10 int hs[2][mod<<3];//hash 11 ll wei[2][mod<<3];//链 12 int ne[2][mod<<3];//表 13 int he[2][mod];//(前向星) 14 ll alans=0; 15 vector<int>sta[2];//储存状态 16 char s[17]; 17 void addstate(int state,ll val)//添加新状态 18 { 19 int k=state%mod; 20 for(int i=he[to][k];i;i=ne[to][i]) 21 { 22 if(hs[to][i]==state) 23 { 24 wei[to][i]+=val; 25 return; 26 } 27 } 28 sta[to].push_back(state); 29 int t=sta[to].size(); 30 ne[to][t]=he[to][k]; 31 wei[to][t]=val; 32 hs[to][t]=state; 33 he[to][k]=t; 34 35 } 36 ll getval(int state)//查询某状态下权值 37 { 38 int k=state%mod; 39 for(int i=he[now][k];i;i=ne[now][i]) 40 if(hs[now][i]==state) 41 return wei[now][i]; 42 } 43 int get(int state,int l)//查询某状态下的某位置的插头类型 44 { 45 return (state>>(l<<1))&3; 46 } 47 int renew(int state,int l,int ss)//更新状态/插头类型 48 { 49 state|=3<<(l<<1); 50 state^=3<<(l<<1); 51 state|=ss<<(l<<1); 52 return state; 53 } 54 int fr1to2(int state,int fr)//两个1插头撞到,找2插头 55 { 56 int p=0,k; 57 for(int i=fr;i<=m+1;++i) 58 { 59 k=get(state,i); 60 if(k==1)++p; 61 if(k==2)--p; 62 if(!p)return i; 63 } 64 } 65 int fr2to1(int state,int fr)//两个2插头撞到,找1插头 66 { 67 int p=0,k; 68 for(int i=fr;i;--i) 69 { 70 k=get(state,i); 71 if(k==1)++p; 72 if(k==2)--p; 73 if(!p)return i; 74 } 75 } 76 void pr(int state)//调试输出 77 { 78 for(int i=1;i<=m+1;++i) 79 { 80 printf("%d",get(state,i)); 81 } 82 printf(" %lld ",getval(state)); 83 } 84 void work(int h,int l) 85 { 86 int state,pl,pu; 87 ll val,ans=0; 88 now^=1;to^=1; 89 printf("%d %d ",h,l); 90 memset(he[to],0,sizeof(he[to]));sta[to].clear(); 91 switch(s[l]) 92 { 93 case '.':{ 94 for(int i=0;i<sta[now].size();++i) 95 { 96 state=sta[now][i]; 97 val=getval(state); 98 pl=get(state,l); 99 pu=get(state,l+1); 100 pr(state); 101 if(!pl&&!pu)//又臭又长的分类讨论 102 { 103 state=renew(state,l,1); 104 state=renew(state,l+1,2); 105 addstate(state,val); 106 } 107 else if(!pl&&pu) 108 { 109 addstate(state,val); 110 state=renew(state,l,pu); 111 state=renew(state,l+1,0); 112 addstate(state,val); 113 } 114 else if(pl&&!pu) 115 { 116 addstate(state,val); 117 state=renew(state,l+1,pl); 118 state=renew(state,l,0); 119 addstate(state,val); 120 } 121 else if(pl==1&&pu==2) 122 { 123 state=renew(state,l+1,0); 124 state=renew(state,l,0); 125 if(!state)ans+=val; 126 } 127 else if(pl==2&&pu==1) 128 { 129 state=renew(state,l+1,0); 130 state=renew(state,l,0); 131 addstate(state,val); 132 } 133 else if(pl==1&&pu==1) 134 { 135 int pos=fr1to2(state,l+1); 136 state=renew(state,l+1,0); 137 state=renew(state,l,0); 138 state=renew(state,pos,1); 139 addstate(state,val); 140 } 141 else if(pl==2&&pu==2) 142 { 143 int pos=fr2to1(state,l); 144 state=renew(state,l+1,0); 145 state=renew(state,l,0); 146 state=renew(state,pos,2); 147 addstate(state,val); 148 } 149 } 150 alans=ans; 151 break; 152 } 153 case '-':{ 154 for(int i=0;i<sta[now].size();++i) 155 { 156 state=sta[now][i]; 157 val=getval(state); 158 pl=get(state,l); 159 pu=get(state,l+1); 160 pr(state); 161 if(pl&&!pu) 162 { 163 state=renew(state,l+1,pl); 164 state=renew(state,l,0); 165 addstate(state,val); 166 } 167 } 168 break; 169 } 170 case '|':{ 171 for(int i=0;i<sta[now].size();++i) 172 { 173 state=sta[now][i]; 174 val=getval(state); 175 pl=get(state,l); 176 pu=get(state,l+1); 177 if(!pl&&pu) 178 { 179 state=renew(state,l,pu); 180 state=renew(state,l+1,0); 181 addstate(state,val); 182 } 183 } 184 break; 185 } 186 case '#':{ 187 for(int i=0;i<sta[now].size();++i) 188 { 189 state=sta[now][i]; 190 val=getval(state); 191 pl=get(state,l); 192 pu=get(state,l+1); 193 if(!pl&&!pu)addstate(state,val); 194 } 195 break; 196 } 197 } 198 } 199 void reset()//换行/行间转移 200 { 201 int state;ll val; 202 now^=1;to^=1; 203 memset(he[to],0,sizeof(he[to]));sta[to].clear(); 204 for(int i=0;i<sta[now].size();++i) 205 { 206 state=sta[now][i]; 207 val=getval(state); 208 if(get(state,m+1))continue; 209 addstate(state<<2,val); 210 } 211 } 212 int main() 213 { 214 scanf("%d%d",&n,&m); 215 to=1;now=0; 216 addstate(0,1); 217 for(int i=1;i<=n;++i) 218 { 219 scanf("%s",s+1); 220 for(int j=1;j<=m;++j)work(i,j); 221 reset(); 222 } 223 cout<<alans<<endl; 224 }
其实这份代码并不完美,它会搜索一些冗余状态,导致程序跑得比较慢,对于极强的数据很容易被卡,正常题一般都能水过
以上为模板,然而我早就不用这套了。。。
插头dp的题核心就在于插头的设计,以及两插头相撞时的处理方式。
插头dp码长好像都挺长,其实都出在了撞插头时的分类讨论以及各种各样的状态处理函数。
题方面的话,插头dp对思路要求不太高,就是恶心。一般会给你一个矩阵,或图之类的,一般行数都挺多,但列数范围不大
在做一些网格题时插头dp可以用来拿部分分。
然后:
学习博客:https://www.cnblogs.com/LadyLex/p/7326874.html
题目总结:https://www.cnblogs.com/Lrefrain/p/12004519.html
插头dp就这么多,可能还是我做题少吧,以后刷的题多了,理解深刻了可能还会再更。