【JZOJ 3853】 帮助Bsny

本文介绍了一个有趣的问题:如何通过移除并重新放置书籍以减少书架的混乱度。使用动态规划的方法,结合二进制技巧,实现了高效求解。文章详细解释了状态转移方程,并提供了完整的代码实现。

Description

Bsny的书架乱成一团了,帮他一下吧!
他的书架上一共有n本书,我们定义混乱值是连续相同高度书本的段数。例如,如果书的高度是30,30,31,31,32,那么混乱值为3;30,32,32,31的混乱值也为3。但是31,32,31,32,31的混乱值为5,这实在是太乱了。
Bsny想尽可能减少混乱值,但他有点累了,所以他决定最多取出k本书,再随意将它们放回到书架上。你能帮助他吗?
100%的数据:1≤k≤n≤100,注意所有书本高度在[25,32]

Analysis

书只有8种,启示我们可以用二进制
这种题可以感觉到用dp做
f[i][j][k][s]表示做完前i个,取了j次书(可能插回前面或后面),最后一个数(没被取走)为k,前面8种书的出现状态为s的最小混乱值
至于记录s的必要就是判断插回前面或后面答案是否要变,且一个位置后面是否有j这种书要预处理来辅助判断。否则会有后效性。
O(n2828)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
const int N=105,INF=2139062143;
int n,m,_2[20],a[N],f[N][N][10][260];
bool ap[N][10];
int main()
{
    _2[0]=1;
    fo(i,1,19) _2[i]=_2[i-1]*2;
    scanf("%d %d",&n,&m);
    fo(i,1,n) scanf("%d",&a[i]),a[i]-=25;
    fd(i,n,1)
    {
        fo(j,0,7) ap[i][j]=ap[i+1][j];
        ap[i][a[i]]=1;
    }
    memset(f,127,sizeof(f));
    int ans=INF;
    f[1][0][a[1]][_2[a[1]]]=1;
    f[1][1][8][0]=0;
    fo(i,1,n)
        fo(j,0,min(i,m))
            fo(k,0,8)
                fo(s,0,_2[8]-1)
                {
                    if(i>j && k==8) continue;
                    int x=f[i][j][k][s];
                    if(x==INF) continue;
                    int &nf=f[i+1][j][a[i+1]][s|_2[a[i+1]]];
                    nf=min(nf,x+1-(a[i+1]==k));
                    int &ng=f[i+1][j+1][k][s];
                    if(i+1<n) ng=min(ng,x+1-ap[i+2][a[i+1]]);
                    bool p=s & _2[a[i+1]];
                    ng=min(ng,x+1-p);
                    if(i+1==n) ans=min(ans,nf);
                    if(i+1==n && j<m) ans=min(ans,ng);
                }
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值