背包第k优解

背包第K优解问题

(这个东西其实早就应该会的,鬼知道怎么就拖到现在。一位学弟说自己写了一道背包k有解的题,想了想突然发现自己不太会,我果然好菜啊……)

  • 写这篇博客的目的不是说A了这道题来讲思路,那样没什么用。毕竟这是提高甚至普及知识点,没什么可吹嘘的。写此文的目的在于总结第k优解这类问题,下面我们来深入讨论一下。
  • 当然,我们仍然以01背包为例。我们在求01背包最优解的时候,有这样一个式子,\(f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])\)。他们两个里面一个会是当前状态\(f[i][j]\)的最优解和次优解。因为目前对这个状态有用的决策只有两个,那么肯定一个是最优解一个是次优解。
  • 推广到第k优解呢?我们把状态拓展一维,\(f[i][j][k]\)表示到第i个物品,使用j的空间的第k优解。对于我们来说,有用的状态还是\(f[i-1][j]\)\(f[i-1][j-v[i]]\),只不过他们此时不再是一个单值了,而是一个有序的序列,对,你把它当成一个数组。我们要求当前状态的前K优解,其实就是从这两个序列里选出k个最优的。那么明显可以用一个双指针,比较加进去就好了。
  • 细节问题在于初始化。最开始我以为反正不会出现负数的情况,直接默认为0,不管不就好了?可是仔细考虑一下f数组的定义,当i=0的时候,只有\(f[0][0][1]\)是合法的状态,其他的都不合法。所以我们需要把f数组初始化为负无穷,使\(f[0][0][1]=0\)\(n*k*v\)递推即可。
#include<bits/stdc++.h>
using namespace std;
int n,m,k,f[5010][51],v[210],w[210],c[51];
int main(){
    scanf("%d%d%d",&k,&m,&n);
    for(int i=1;i<=n;++i) scanf("%d%d",&v[i],&w[i]);
    memset(f,0xcf,sizeof(f));
    f[0][1]=0;
    for(int i=1;i<=n;++i){
        for(int j=m;j>=v[i];--j){
            int l=1,r=1,t=0,tmp=0;
            while(t<k){
                if(f[j][l]<f[j-v[i]][r]+w[i])
                c[++tmp]=f[j-v[i]][r]+w[i],r++;
                else c[++tmp]=f[j][l],l++;
                t++;
            }
            for(int o=1;o<=k;++o) f[j][o]=c[o];
        }
    }int ans=0;
    for(int i=1;i<=k;++i) ans+=f[m][i];
    cout<<ans<<endl;
    return 0;
}
speech.gif posted on 2019-03-07 20:57 kgxpbqbyt 阅读( ...) 评论( ...) 编辑 收藏
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值