[Noip模拟题]帮助Bsny
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个整数表示每本书的高度。
1≤k≤n≤100,注意所有书本高度在[25,32]
Output
仅一行一个整数,表示能够得到的最小混乱值。
Sample Input
5 1
25 26 25 26 25
Sample Output
3
Sol
N高度差很小,可以对这个宽度只有8的高度差状态压缩进行DP。每本书抽出来后,要么放在和它等高的书旁边,要不放在一边作为一个新的高度。
状态为:f[i][j][k][mask],表示前i本书已经抽出了j本,前i本中没被抽出的书里最后一本书的高度是k,mask是一个0~2^8-1的二进制,表示前i本中没被抽出的书里高度的存在情况。整体表示前i本书中没被抽出的书组成的序列的最小混乱度。
然后枚举第i本书是否被抽出。
#include<bits/stdc++.h> using namespace std; int a[110],f[101][101][9][1<<8]; int main() { // freopen("help.in","r",stdin); // freopen("help.out","w",stdout); memset(f,0x3f,sizeof(f)); int n,k,ss=0; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",a+i); a[i]-=25; ss|=1<<a[i]; } f[0][0][8][0]=0; //f[i][j][k][mask],表示前i本书已经抽出了j本, //前i本中没被抽出的书里最后一本书的高度是k,mask是一个0~2^8-1的二进制, //表示前i本中没被抽出的书里高度的存在情况。 //整体表示前i本书中没被抽出的书组成的序列的最小混乱度。 for(int i=0;i<n;i++) //前i本书 for(int j=0;j<=k;j++) //抽走了j本 for(int l=0;l<=8;l++) //没抽走了,最后一本的高度 for(int s=0;s<1<<8;s++) //没抽走了,存在哪些高度 { //第i个没有被抽走 f[i+1][j][a[i+1]][s|1<<a[i+1]]=min(f[i+1][j][a[i+1]][s|1<<a[i+1]],f[i][j][l][s]+(l!=a[i+1])); if(j!=k) //如果还没有抽走K本,则第j本书是可以抽走的 f[i+1][j+1][l][s]=min(f[i+1][j+1][l][s],f[i][j][l][s]); } int ans=1e9; for(int s=0;s<1<<8;s++) //枚举高度集合,没有抽走的 { int t=s^ss,cnt=0; //ss代表全集,t代表抽走的书的状态,如果本在没有抽走中的,为0,反之为1 for(int i=0;i<8;i++) //计算T集合的大小 cnt+=t>>i&1; for(int l=0;l<=8;l++) ans=min(f[n][k][l][s]+cnt,ans); } printf("%d\n",ans); return 0; }