状压dp专题
状压dp
将状态表示为一个或多个n进制数,通过数位的运算判断情况之间是否合法,从而完成状态的转移。
矩阵内状压dp一般模板
伪代码
将每一行的状态用一个二进制数表示
a{i}.s存储第i中合法情况的二进制数
f{i}{j}表示第i行状态为第j种状态时的答案(最大值or方案数)
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
scanf("%d",&a);
mapp[i]=mapp[i]+a;
}//生成原始矩阵的情况,mapp[i]表示当前行的状态
for(int i=0;i<(1<<m);++i)
{
if(~~~~)
{
cnt++;
a[cnt].s=i;
}
}
//预处理对于所有行的可能合法情况,同时存储在数组中,可以有效避免MLE
for(int i=1;i<=n;++i)//枚举行数
for(int k=1;k<=cnt;++k)//枚举当前行即第i行的状态
if(a[k].s对于mapp[i]合法)
for(int q=1;q<=cnt;++q)//枚举上一行的状态
if(a[q].s对于mapp[i-1]合法 && a[q].s对于a[k].s合法
)
f[i][k]=max(f[i][k],f[i-1][q]);
for(int i=1;i<=cnt;++i)
ans=max(ans,f[n][i])
//由于最后一行的左右情况都可能成为答案,因此需要判断
例题:
洛谷P1879 [USACO06NOV]Corn Fields G
洛谷P2704炮兵阵地
Code第一题:
因为数据较小因此直接用数组下标表示状态
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=1e8;
int n,m;
int a[13][13];
int F[13];
int f[13][1<<12+5];
bool g[1<<12+5];
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=m;++i)
{
for(int j=1;j<=n;++j)
{
scanf("%d",&a[i][j]);
F[i]=(F[i]<<1)+a[i][j];
}
}
for(int i=0;i<(1<<n);++i)
g[i]=(!(i&(i<<1))) && (!(i&(i>>1)));
f[0][0]=1;
for(int i=1;i<=m;++i)
for(int j=0;j<(1<<n);++j)
if(g[j]&&(j&F[i])==j)
for(int k=0;k<(1<<n);k++)
if((k&j)==0)
f[i][j]=(f[i][j]+f[i-1][k])%mod;
int ans=0;
for(int i=0;i<(1<<n);++i)
{
ans=(ans+f[m][i])%mod;
}
printf("%d",ans);
return 0;
}
Code
第二题:
数据较大因此先把统计状态数,另开一个数组存储状态
#include<bits/stdc++.h>
using namespace std;
int n,f[105][105][105],m,mapp[105],cnt=0;
struct mint
{
int s,num;
}a[105];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
char c;
cin>>c;
mapp[i]=(mapp[i]<<1)+(c=='P');
}
for(int i=0;i<(1<<m);++i)
{
if((i&(i<<1)) || (i&(i<<2)) || (i&(i>>1)) || (i&(i>>2))) continue;
cnt++;
a[cnt].s=i;
for(int j=0;(1<<j)<=i;++j) a[cnt].num += !!(i & (1<<j));
if((i&mapp[1]) == i) f[1][0][cnt]=a[cnt].num;
}
for(int i=1;i<=cnt;++i)
{
for(int j=1;j<=cnt;++j)
{
if(!(a[i].s&a[j].s) && (mapp[2]&a[j].s)==a[j].s) f[2][i][j]=f[1][0][i]+a[j].num;
}
}
for(int i=3;i<=n;++i)
for(int j=1;j<=cnt;++j)
if((mapp[i]&a[j].s) == a[j].s)
for(int k=1;k<=cnt;++k)
if(!(a[k].s&a[j].s))
for(int l=1;l<=cnt;++l)
{
if(!(a[l].s&a[k].s) && !(a[j].s&a[l].s)) f[i][k][j]=max(f[i][k][j],f[i-1][l][k]+a[j].num);
}
int ans=0;
for(int i=1;i<=cnt;++i)
for(int j=1;j<=cnt;++j)
ans=max(ans,f[n][i][j]);
printf("%d\n",ans);
return 0;
}