状态压缩其实就是用特殊的方法来表示复杂的状态做DP,这种特殊的方法一般是位运算。
例题一:种花小游戏
DPi j ,i可以拆分成二进制数,每一位上的0或1表示是否走过,j表示当前位置。
#include<bits/stdc++.h>
using namespace std;
const double maxn=200000000.0;
double f[600010][30],jl[30][30],ans=maxn;
int x[30],y[30],sum=0,n;
double DP(int now,int st)
{
if(f[now][st]<maxn) return f[now][st]; //记忆化;
for(int i=1;i<=n;i++)
{
if((i!=st)&&((1<<(i-1))&now)) //枚举上一次在哪里;
f[now][st]=min(f[now][st],jl[i][st]+DP(now-(1<<(st-1)),i));
}
return f[now][st];
}
void work()
{
for(int i=1;i<=n;i++) //求一个上限;
sum+=(1<<(i-1));
for(int i=1;i<=sum;i++)
for(int j=1;j<=n;j++)
f[i][j]=maxn;//赋初值;
for(int i=1;i<=n;i++)
f[(1<<(i-1))][i]=0;
for(int i=0;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
jl[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
jl[j][i]=jl[i][j];
}
for(int i=1;i<=n;i++)
{
ans=min(ans,jl[0][i]+DP(sum,i));
// printf("%lf\n",DP(sum,i));
}
printf("%.2lf",ans);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
}
scanf("%d%d",&x[0],&y[0]);
work();
return 0;
}
很水。
例题二:广场铺砖
#include<bits/stdc++.h>
using namespace std;
long long sum=0,f[30][3000],w,h;
void dp(int i,int st,int now,int next,int d)
//第几行,初始状态,现在状态,下一行状态,这行第几个;
{
if(now==sum) //这一行铺完了;
{
f[i+1][next]+=f[i][st]; 把方法数加到能够达到的下一行状态的方法数上;
return;
}
if((now&(1<<(d-1)))==0) 这个位置可以铺
{
dp(i,st,now|(1<<(d-1)),next|(1<<(d-1)),d+1); //竖着铺,影响下一行;
if(((now&(1<<(d)))==0)&&d+1<=h) //如果这个位置下一个也可以铺;
dp(i,st,now|(1<<d)|(1<<(d-1)),next,d+2); //横着铺,对下一行没有影响;
}
else
dp(i,st,now,next,d+1); //不能铺;
}
int main()
{
scanf("%d%d",&w,&h);
sum=(1<<h)-1;
f[1][0]=1;
for(int i=1;i<=w;i++)
for(int j=0;j<=sum;j++)
dp(i,j,j,0,1);
printf("%lld",f[w+1][0]);
// 最后一行的方法数也会传给下一行,当可能状态是0时意味着刚好铺满;
return 0;
}