#状压dp#JZOJ 3853 帮助Bsny
题目
一共有\(n\)本书,混乱值是连续相同高度书本的段数。
可以取出\(k\)本书随意放回,问最小混乱值,高度\([25\sim 32]\)
分析
设\(f[i][j][k][mask]\)表示前\(i\)本书已经抽出了\(j\)本,前\(i\)本中没被抽出的
书里最后一本书的高度是\(k\),\(mask\)是一个\(0\sim 2^8-1\)的二进制,表示前\(i\)本中没被
抽出的书里高度的存在情况。整体表示前\(i\)本书中没被抽出的书组成的序列的最小混乱度。
然后枚举第\(i\)本书是否被抽出。
代码
#include <cstdio>
#include <cstring>
#define rr register
using namespace std;
const int po[9]={1,2,4,8,16,32,64,128,256}; bool v[101];
int b[101],dp[2][101][11][261],top,c[101],t[101],n,m,a[101];
inline void Min(int &a,int b){if (a>b) a=b;}
signed main(){
scanf("%d%d",&n,&m);
for (rr int i=1;i<=n;++i){
scanf("%d",&a[i]),a[i]-=24;
if (a[i]!=a[i-1]) b[++top]=a[i];
++c[top];
}
for (rr int i=top;i;--i){
if (v[b[i]]) ++t[i];
v[b[i]]=1;
}
memset(dp[0],42,sizeof(dp[0]));
dp[0][0][0][0]=0;
for (rr int i=1;i<=top;++i){
memset(dp[i&1],42,sizeof(dp[i&1]));
for (rr int j=0;j<=m;++j)
for (rr int k=0;k<9;++k)
for (rr int p=0;p<po[8];++p){
if (dp[(i&1)^1][j][k][p]>n) continue;
Min(dp[i&1][j][b[i]][p|po[b[i]-1]],dp[(i&1)^1][j][k][p]+(b[i]!=k));//不动
if (j+c[i]>m) continue;
Min(dp[i&1][j+c[i]][k][p|po[b[i]-1]],dp[(i&1)^1][j][k][p]+!(p&po[b[i]-1]));//放前面
if (t[i]) Min(dp[i&1][j+c[i]][k][p],dp[(i&1)^1][j][k][p]);//放后面
}
}
rr int ans=2e9;
for (rr int i=0;i<=m;++i)
for (rr int j=0;j<9;++j)
for (rr int k=0;k<po[8];++k)
Min(ans,dp[top&1][i][j][k]);
return !printf("%d",ans);
}