#状压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);
}
posted @ 2020-02-15 14:36  lemondinosaur  阅读(102)  评论(0编辑  收藏  举报