JZOJ 3853. 【NOIP2014八校联考第2场第2试9.28】帮助Bsny(help)

18 篇文章 0 订阅
9 篇文章 0 订阅

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%的数据: 1n20k=1
40%的数据:书的高度不是25就是32,高度种类最多2种。
100%的数据: 1kn100 ,注意所有书本高度在 [25,32]

Solution

  • 这题的亮点就是书的高度高度密集,于是敏感地想到状压DP

  • 先把高度减去24 方便处理,再把书分成高度相同的几段,记录段数和每一段的长度

  • 设状态 f[i][j][k][l] 表示,前 i 本书中,抽出了 j 本,没抽出的书里最后一本的高度是 k ,没抽出的书状态是 l ,整体的最小混乱度。

  • 那么第一种情况:第 i 本不抽出,转移显然;

  • 第二种情况:第 i 本抽出,则又分两种小情况:

    1. 抽出的书放到前面,则转移可以通过状态 l 得到;
    2. 抽出的书放到后面,则转移可以通过预处理后面得到。
  • 最后数组可以开滚动,总时间复杂度为 O(288NK)

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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值