Description
Bsny的书架乱成一团了,帮他一下吧!
他的书架上一共有n本书,我们定义混乱值是连续相同高度书本的段数。例如,如果书的高度是30,30,31,31,32,那么混乱值为3;30,32,32,31的混乱值也为3。但是31,32,31,32,31的混乱值为5,这实在是太乱了。
Bsny想尽可能减少混乱值,但他有点累了,所以他决定最多取出k本书,再随意将它们放回到书架上。你能帮助他吗?
Input
第一行两个整数n,k,分别表示书的数目和可以取出的书本数目。
接下来一行n个整数表示每本书的高度。
Output
仅一行一个整数,表示能够得到的最小混乱值。
Sample Input
5 1
25 26 25 26 25
Sample Output
3
Data Constraint
20%的数据:
1≤n≤20,k=1
。
40%的数据:书的高度不是25就是32,高度种类最多2种。
100%的数据:
1≤k≤n≤100
,注意所有书本高度在
[25,32]
。
Solution
这题的亮点就是书的高度高度密集,于是敏感地想到状压DP
先把高度减去24 方便处理,再把书分成高度相同的几段,记录段数和每一段的长度
设状态 f[i][j][k][l] 表示,前 i 本书中,抽出了
j 本,没抽出的书里最后一本的高度是 k ,没抽出的书状态是l ,整体的最小混乱度。那么第一种情况:第 i 本不抽出,转移显然;
第二种情况:第
i 本抽出,则又分两种小情况:- 抽出的书放到前面,则转移可以通过状态 l 得到;
- 抽出的书放到后面,则转移可以通过预处理后面得到。
最后数组可以开滚动,总时间复杂度为
O(28∗8∗N∗K)
Code
#include<cstdio>
#include<cstring>
#define F f[1^roll][j][k][l]
using namespace std;
const int N=101;
int num,roll;
int a[N],b[N],c[N],d[N],p[9];
bool bz[N];
int f[2][N][9][1<<8];
inline int read()
{
int data=0; char ch=0;
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
return data;
}
inline void work(int &x,int y){if(x>y) x=y;}
int main()
{
int n=read(),m=read();
for(int i=p[0]=1;i<=8;i++) p[i]=p[i-1]*2;
for(int i=1;i<=n;i++)
{
a[i]=read()-24;
if(a[i]!=a[i-1]) b[++num]=a[i];
c[num]++;
}
for(int i=num;i;i--)
{
if(bz[b[i]]) d[i]++;
bz[b[i]]=true;
}
memset(f[0],60,sizeof(f[0]));
f[0][0][0][0]=0;
for(int i=1;i<=num;i++)
{
roll=1^roll;
memset(f[roll],60,sizeof(f[roll]));
for(int j=0;j<=m;j++)
for(int k=0;k<9;k++)
for(int l=0;l<p[8];l++)
{
if(F>n) continue;
work(f[roll][j][b[i]][l|p[b[i]-1]],F+(b[i]!=k));
if(j+c[i]>m) continue;
work(f[roll][j+c[i]][k][l|p[b[i]-1]],F+!(l&p[b[i]-1]));
if(d[i]) work(f[roll][j+c[i]][k][l],F);
}
}
int ans=1e9;
for(int j=0;j<=m;j++)
for(int k=0;k<9;k++)
for(int l=0;l<p[8];l++)
work(ans,f[roll][j][k][l]);
printf("%d",ans);
return 0;
}