[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;
}

  

posted @ 2020-07-27 11:17  我微笑不代表我快乐  阅读(203)  评论(0编辑  收藏  举报