图片加载可能有点慢,请跳过题面先看题解,谢谢
首先我们很容易注意到一件事情:\(h\) \(\epsilon\) {\(25,26,27,28,29,30,31,32\)},数一下,\(8\)个数,上状压
设状态 \(f[i][j][s][g]\) 为,处理到第 \(i\) 本书,抽掉了$ j$ 本,剩余书的状态为 \(s\) (一个二进制数),最后一本放 \(g\) 的最小步数
我们分三种情况转移:
- 第 \(i\) 本和第 \(i-1\) 本相同,则有:\(f[i][j][s][g]=min(f[i-1][j][s][g])\);
- 第 \(i\) 本和第 \(i-1\) 本不同,且抽掉第 \(i\) 本,则有:\(f[i][j+1][s][g]=min(f[i-1][j][s][g])\);
- 第 \(i\) 本和第 \(i-1\) 本不同,且不抽掉第 \(i\) 本,则有:\(f[i][j][s|(1<<(a[i]-25))][a[i]-25]=min(f[i-1][j][s][g]+1)\);
其中,\((1<<g)\leq s\),\(s\) \(\epsilon\) \(S\),\(S\) 为当前所有出现过的书的状态,\(s\) 可以通过枚举 \(S\) 的子集得到
当然,第一维可以滚一维,节省空间,也可以节省 \(memset\) 的时间
最后答案就是 \(min(f[n][j][s][g]+calc(S\) ^ \(s))\),\(calc(S\) ^ \(s)\) 为 \(S\) ^ \(s\) 中 \(1\) 的个数
//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define inf (0x3f3f3f3f)
#define il inline
#define RG register
using namespace std;
il int gi(){ RG int x=0,q=1; RG char ch=getchar(); while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
if( ch=='-' ) q=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; }
int S,x,t;
int n,k,f[110][110][1<<8][10];
il void init(){ S=0; memset(f,0x3f,sizeof(f)); }
il int min(int &a,int b){return a=a<b?a:b;}
il int calc(int c){ return (!c)?0:calc(c/2)+(c%2); }
il void work(){
for(int i=1;i<=n;i++){
scanf("%d",&x); x-=25;
f[i][i-1][1<<x][x]=1;
for(int j=0;j<=min(i-1,k);j++)
for(int s=S;s;s=(s-1)&S)
for(int g=0;(1<<g)<=s;g++)
if(f[i-1][j][s][g]!=inf){
int y=f[i-1][j][s][g];
if(g==x) min(f[i][j][s][g],y);
else{
min(f[i][j+1][s][g],y);
min(f[i][j][s|(1<<x)][x],y+1);
}
}
S|=(1<<x);
}
int ans=inf;
for(int j=0;j<=k;j++)
for(int s=S;s;s=(s-1)&S)
for(int g=0;(1<<g)<=s;g++)
if(f[n][j][s][g]!=inf)
ans=min(ans,f[n][j][s][g]+calc(S^s));
printf("Case %d: %d\n\n",++t,ans);
}
int main(){ while(scanf("%d%d",&n,&k)&&n){ init(); work(); } return 0; }