题目:click
题意:给定n个书本的高度,若一个区间有连续的高度相同则示为一个混乱度(只有一个也占一个混乱度),告诉你能拿k本书之后放回去,问最小混乱度。
题目中说明了书的高度25-32,加上n和k的大小,可以进行状态压缩dp,
再来观察每一本书,当到第i本书的时候我们可以选择拿或者不拿,将25-32压缩状态,由于每次拿第i本书的时候需要考虑到前一本书的状态,再设置一个最后的书本高度,dp[i][j][s][h]:表示前i本书 拿了j本出来 剩余的书本状态s(0—1<<8) 最后一本书的高度h 的最小混乱度。
进行分析状态转移方程:
当第i本书我选择拿的时候:
我选择不拿第i本书,但是需要确定一下之前最后一本书的高度是否相同。
精妙精妙精妙的状压dp,把所有的书本状态用一个变量表示出来,再用该变量与枚举s进行对比,相互异或可以得到拿走的书,0-1的相互改变表示它拿走书的混乱度变化从这里切入,只需要计算异或的结果1的个数即混乱度。
奥 题目卡内存,注意到只有两层的状态相互转移,改为滚动数组即可。
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
int n,k;
int a[110];
int dp[3][110][(1<<8)][10];// 前i个 拿走了j个 剩余的书本的状态s 最后一位的高度temp 的mess MIN
int main()
{
int yy=1;
while(~scanf("%d %d",&n,&k)&&(n||k))
{
memset(dp,inf,sizeof(dp));
int i,j,s;
int hh=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]-=25;
hh|=(1<<a[i]);
}
dp[1][1][0][8]=0;//没有任何书放在桌子上
dp[1][0][0|(1<<a[1])][a[1]]=1;
for(i=2;i<=n;i++)
{
memset(dp[(i)%2],inf,sizeof(dp[(i)%2]));
for(j=0;j<=k&&j<i;j++)
{
for(s=0;s<(1<<8);s++)
{
for(int temp=0;temp<=8;temp++)
{
if(dp[(i+1)%2][j][s][temp]==inf)//不存在这种状态
continue;
if(j<k)//取走当前得这本书
{
dp[i%2][j+1][s][temp]=min(dp[i%2][j+1][s][temp],dp[(i+1)%2][j][s][temp]);
}
//不拿
if(a[i]==temp)
{
dp[i%2][j][s][temp]=min(dp[i%2][j][s][temp],dp[(i-1)%2][j][s][temp]);
}
else
dp[i%2][j][s|(1<<a[i])][a[i]]=min(dp[i%2][j][s|(1<<a[i])][a[i]],dp[(i-1)%2][j][s][temp]+1);
}
}
}
}
int ans=n;
for(j=0;j<=k;j++)
{
for(s=0;s<(1<<8);s++)
{
for(int temp=0;temp<8;temp++)
{
int cnt=0;
int tmp=hh^s;
while(tmp)
{
if(tmp&1)
cnt++;
tmp>>=1;
}
ans=min(ans,dp[n%2][j][s][temp]+cnt);
}
}
}
printf("Case %d: %d\n\n",yy++,ans);
}
return 0;
}