NOIP模拟——偷书

在L的书架上,有N本精彩绝伦的书籍,每本书价值不菲。
M是一个书籍爱好者,他对L的书籍早就垂涎三尺。最后他忍受不了诱惑,觉得去偷L的书,为了迅速完成这件事,同时他不希望L很快发现书籍少了,他决定偷书时,对于任意连续的k本书,他最多选B本,最少选A本。现在他想知道怎么选出来的书本最后使得偷的书籍的价值和,与剩下的书籍价值和,差值最大。
输入
第一行四个整数 n,k,a,b
一行 N 个整数表示每本书的价值
输出
一个整数表示答案
样例输入
2 1 0 1
2 -2
样例输出
4
提示
得到第一本书 得到的价值和是 2
剩余的价值和是-2
差值为 4
对于 20%:n<=10
对于另外 20%:a=0,b=k
对于 100%:n<=1000,0<=a<=b<= k<=10 ,所有书籍的价值的绝对值<=10^9

看到k小于等于10就应该想到状压dp的

dp[i][j]表示当前在第i个,状态为j的最优解,因为每次向后递推一个,我们只需要维护最后一个是否选了,并且判断现在这个选不选

结果被数组大小给卡了,真是那啥了那啥

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int v[1055],n,k,b,a,num[1024];
ll dp[1055][1024],sum;
inline void getn(){
    for(int i=0;i<1024;i++)
    {
        int t=i,tot=0;
        while(t)
        {
            if(t&1)
            {
                tot++;
            }
            t/=2;
        }
        num[i]=tot;
    }
}
inline bool judge(int k)
{
    return !(num[k]<a||num[k]>b);
}
int main(){
    scanf("%d%d%d%d",&n,&k,&a,&b);
    getn();
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&v[i]);sum+=v[i];
    }
    for(int i=0;i<(1<<k);i++)
    {
        for(int j=0;j<k;j++)
        {
            if(i&(1<<j)) dp[k][i]+=v[k-j];
        }
    }
    for(int i=k+1;i<=n;i++)
    {
        for(int j=0;j<(1<<k);j++)
        {
            if(!judge(j)) continue;
            int st=j;
            st>>=1;
            if(j&1)
            {
                int sta=st+(1<<k-1);
                dp[i][j]=dp[i-1][sta]+v[i];
                if(judge(st)) dp[i][j]=max(dp[i][j],dp[i-1][st]+v[i]);
            }
            else
            {
                int sta=st+(1<<k-1);
                dp[i][j]=dp[i-1][st];
                if(judge(sta)) dp[i][j]=max(dp[i][j],dp[i-1][sta]);
            }
        }
    }
    ll ans=-1000000000000;
    for(int i=0;i<(1<<k);i++)
    {
        if(judge(i))
        ans=max(ans,dp[n][i]);
    }
    cout<<ans-sum+ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42555009/article/details/82929911