题目大意
有n本书,最多k次操作,每次操作可以把一本书拿出来,放到一个位置去,有一个指标较mess度,他是书的高度的段数,连续的书高度一样算一段,现在给你最先开始各个位置上的书的高度,求操作后最小的mess度。
分析
首先我们要注意一个非常非常重要的条件就是书的高度的范围很小。所以我们不由想到了状压dp。我们再仔细思考一下不难想到dp[i][j][msk][k]表示考虑到第i本,挪动了j次,没挪动的书的高度集合为msk,没挪动的书的最后一本高度为k。然后我们便可以转移(具体见代码),而最后答案要将dp值加上对于所有高度为h的书都挪动了的不同h的数量。注意数组不要开小啦。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
#define gm(x,y) x=min(x,y)
const int inf = 0x3f3f3f3f;
int n,m,sum,dp[2][105][260][10],apr[50],c[105],cse,now;
inline void init(){
memset(apr,0,sizeof(apr));
sum=0;
cse++;
}
inline void deal(int pl,int x){
if(!apr[x]){
sum++;
apr[x]=sum;
}
c[pl]=apr[x];
return;
}
inline int Sum(int msk){
int res=0;
for(int i=0;i<sum;i++)
if(((1<<i)&msk)==0)
res++;
return res;
}
inline void DP(){
int i,j,k,s;
now=1;
memset(dp[1],0x3f,sizeof(dp[1]));
dp[1][0][(1<<(c[1]-1))][c[1]]=1;
dp[1][1][0][0]=0;
for(i=1;i<n;i++){
now^=1;
memset(dp[now],0x3f,sizeof(dp[now]));
for(j=0;j<=m;j++)
for(s=0;s<(1<<sum);s++)
for(k=0;k<=sum;k++)if(dp[now^1][j][s][k]<inf){
//cout<<i<<' '<<j<<' '<<s<<' '<<k<<' '<<dp[now^1][j][s][k]<<endl;
if(k==c[i+1])
gm(dp[now][j][s][k],dp[now^1][j][s][k]);
else
gm(dp[now][j][s|(1<<(c[i+1]-1))][c[i+1]],dp[now^1][j][s][k]+1);
if(j<m)gm(dp[now][j+1][s][k],dp[now^1][j][s][k]);
}
}
return;
}
inline void getans(){
int ans=inf,i,j,k;
for(i=0;i<=m;i++)
for(j=0;j<(1<<sum);j++)
for(k=1;k<=sum;k++)
gm(ans,dp[now][i][j][k]+Sum(j));
printf("Case %d: %d\n\n",cse,ans);
}
int main(){
int i,j,k;
cse=0;
scanf("%d%d",&n,&m);
while(n&&m){
init();
for(i=1;i<=n;i++){
scanf("%d",&c[i]);
deal(i,c[i]);
}
DP();
getans();
scanf("%d%d",&n,&m);
}
return 0;
}