\(\mathcal{Description}\)
Bubu的书架乱成一团了!帮他一下吧!
他的书架上一共有n本书。我们定义混乱值是连续相同高度书本的段数。例如,如果书的高度是30,30,31,31,32,那么混乱值为3,30,32,32,31的混乱度也是3,但31,32,31,32,31的混乱度是5,这实在是太乱了。
Bubu想尽可能地减少混乱度,但他有点累了,所以他决定最多取出k本书,再随意将它们放到书架上。你能帮助他吗?
\(\mathcal{Solution}\)
考虑取出一本书把其认为是删掉这本书,因为总有办法使其不会影响到后面的取书过程
特殊的是如果将一种书全部删掉,那么应该还是会有1的混乱度
如果考虑\(dp\)方式为如何删书,那么这个过程实在复杂
如果考虑最后剩下的书,那么复杂度是\(2^n\)妥妥的\(T\)
但是仔细的想一下,会发现,考虑最后剩下的书时,并不是前面所有的状态都对现在的状态会有影响
即如果一本书隔得较远,则与你当前这本书没有多大关系,在保证所有的状态会被考虑在内的情况下,考虑状态压缩
设\(f[i][j][s][l]\)表示前\(i\)本书,删掉了\(j\)本,剩下书的种类的集合为\(s\),上一本没有被删的书的种类是\(l\)
考虑第\(i\)本书
- 若删掉这本书,则有\(f[i][j+1][s][l]=min\left(f[i][j][s][l],]f[i-1][j][s][l]\right)\)
- 若保留这本书,则有\(f[i][j][s|(1<<h[i])][h[i]]=min\left(f[i][j][s|(1<<h[i])][h[i]],f[i-1][j][s][l]+\left(h[i]==l?0:1\right)\right)\)
最后的时候回来考虑剩下的书的种类集合,如果原本所有书的种类集合\(S\)有一种书,现在没有了,那么我们就多减了一个1,加回来就是
\(\mathcal{Code}\)
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年10月04日 星期五 08时36分03秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cstring>
#define inf 0x7f7f7f7f
using namespace std;
const int maxn = 111;
const int lim = 256;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
int n,k,cas;
int h[maxn],one[lim+5];
int f[2][maxn][lim+5][11];
bool now,pre;
//f[i][j][k][l] 前i本书选了j本书,存在的书的种类集合为k,上一本书为l
int main()
{
for (int i=0;i<lim;++i){
for (int j=0;j<8;++j)
if (i&(1<<j)) ++one[i];
}
while (true){
cin>>n>>k;
if (n+k==0) break;
int S=0,mh=0;
for (int i=1;i<=n;++i){
cin>>h[i];
h[i]-=25;
S|=1<<h[i];
mh=max(mh,h[i]);
}
++mh;
memset(f[0],0x7f,sizeof(f[0]));
now=0;
f[0][0][1<<h[1]][h[1]]=1;
f[0][1][0][mh]=0;
for (int i=2;i<=n;++i){
now^=1,pre=!now;
memset(f[now],0x7f,sizeof(f[now]));
for (int j=0;j<=k;++j){
for (int s=0;s<=S;++s)
for (int l=0;l<=mh;++l)
if (f[pre][j][s][l]!=inf){
f[now][j][s|(1<<h[i])][h[i]]=min(f[now][j][s|(1<<h[i])][h[i]],f[pre][j][s][l]+(h[i]==l?0:1));
f[now][j+1][s][l]=min(f[now][j+1][s][l],f[pre][j][s][l]);
}
}
}
int ans=inf;
for (int j=0;j<=k;++j)
for (int s=0;s<=S;++s)
for (int l=0;l<mh;++l)
if (f[now][j][s][l]!=inf){
int take=S^s;
ans=min(ans,f[now][j][s][l]+one[take]);
}
printf("Case %d: %d\n\n",++cas,ans);
}
return 0;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧