膜了一波WXH天犇的代码,woc,惊为天人,代码还能这么写的!?
插头DP
- 插头DP的状压是要把整行的插头情况全部状压进去,所以说插头DP也是状压DP的一种(不要尝试不压缩)
- 插头DP大多逐格递推,当前格仅需要上一格递推,所以可以滚动。
- 插头DP是插在边上的插头,不是点上,因此有m个下插头以及一个右插头,注意空间。
- 插头DP的关键在于确定插头的种类(包括没有插头),很多时候会超过2种也就是说不- - 能用二进制来压位固然简单,不能还不是当做对应进制压位。
- 插头DP的时间复杂度是 n∗m∗Km n ∗ m ∗ K m ,所以要通过翻转使得m总是不比n大,其中K是插头种类数量(标号:0~K-1,其中0是代表没有插头)。
滚动数组的技巧:
- 这里一般来说都要滚动的,用一个cur|cur=0或1,代表已经填完了的那一格子填在了哪一排,每填完一格cur^1
- 我们转移的时候实际上用的是刷表法,K枚举的实际上是上一排的情况,来刷这一排的情况。
- 其中还有一格比较重要的点就是每次填当前表的时候都要memset(dp[cur^1]),不清空没法刷表
- 最终答案一般就是dp[cur][0]
压位的技巧:
- 很多压2的幂次位然后使劲hash,WXH表示这很蠢直接写K进制即可,类比二进制/十进制都可以
- 具体操作是用一个table数组,table[i]表示i^K
注意:以下我们说的存储全部是基于右边是低位,左边是高位的写数意识,最右边是第一位。
- 我们把第一位用来存右插头位置,这样不会涉及到位运算,非常方便。
- 然后table[j]就是第j个格子在进制中对应的基本单位了,比如说005000,那么table[3]就是1000,刚好就是第三个格子的值(因为第一位是用右插头表示的,所以实际是第四位)
- 要提取第j位的元素就是S/table[j]%K; 有插头的元素是s%K;
转移的技巧:
- 讨论的时候如果有BASE个插头(算上没有插头),那么久老老实实讨论Base^2种情况,其中有些情况无法转移,所以情况会减少一点。
- 其中还要讨论左端点的特殊情况,对于右插头不是空的情况基本可以continue,在刷表的时候当前元素没有贡献也可以continue
- 枚举插头情况的时候要枚举到table[m+1]-1
一个代码(BZOJ 2331: [SCOI2011]地板)
#include<bits/stdc++.h>
using namespace std;
const int maxn=105,maxv=177147+105,mod=20110520;
int n,m,to;//要保证m比较小;
char op[maxn];
char s[maxn][maxn];
int dp[maxn][maxv],table[maxn],cur;
inline void upd(int &x,int y)
{
x+=y;
if(x>=mod)x-=mod;
}
void Init()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",op+1);
for(int j=1;j<=m;j++)
{
if(n>m)
s[i][j]=op[j];
else
s[j][i]=op[j];
}
}
if(m>n)swap(n,m);
table[0]=1;
for(int i=1;i<=m+1;i++)table[i]=table[i-1]*3;
}
void MakeDp()
{
cur=0;
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++,cur^=1)
{
memset( dp[ cur^1 ], 0, sizeof (dp[ cur^1 ]) );
for(int k=0;k<table[m+1];k++)if(dp[cur][k])
{
int left=k%3,up=k/table[j]%3;
int t=dp[cur][k];
if(s[i][j]=='*')
{
if(!left && !up)upd(dp[cur^1][k],t);
continue;
}
if(j==1 && left)
continue;
if(!left)
{
if(!up)
{
upd(dp[cur^1][k+1],t);
upd(dp[cur^1][k+table[j]*2+2],t);
upd(dp[cur^1][k+table[j]],t);
}
else if(up==1)
{
upd(dp[cur^1][k],t);
upd(dp[cur^1][k-table[j]+2],t);
}
else
{
upd(dp[cur^1][k],t);
upd(dp[cur^1][k-table[j]*2],t);
}
}
else if(left==1)
{
if(!up)
{
upd(dp[cur^1][k+table[j]*2-1],t);
upd(dp[cur^1][k],t);
}
else if(up==1)
{
upd(dp[cur^1][k-table[j]-1],t);
}
}
else
{
if(!up)
{
upd(dp[cur^1][k],t);
upd(dp[cur^1][k-2],t);
}
}
}
}
}
printf("%d\n",dp[cur][0]);
}
int main()
{
freopen("in.txt","r",stdin);
Init();
MakeDp();
return 0;
}