背包第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;
}

猜你喜欢

转载自www.cnblogs.com/kgxw0430/p/10492293.html