1. 程式人生 > >第K大01背包

第K大01背包

真的 .... scanf ack 但是 != 可能 math cst

其實這個問題,真的挺好想的,但是我咋想了那麽久呢~~

很好理解,第K大01背包一定基於01背包,dp數組也很容易的想到由dp[V] ----> dp[V][K],來表示背包容量是V時候的第K大背包

然後就是狀態轉移方程了,多寫一寫,你也能手推出來的,不能被嚇到

dp[V][1] = max_第一大(dp[v][1],dp[v-w][1]+vi)

dp[V][2] = max_第二大數(dp[v][1],dp[v-w][1]+vi,dp[v][2],dp[v-w][2]+vi) = max_第二大數(dp[v][1],dp[v][2],dp[v-w][2]+vi)

從而得到一般式子

dp[v][k] = max_第K大(dp[v][1],dp[v][2],....dp[v][k],dp[v-w][1]+vi,.....dp[v-w][k]+vi

明白了這兩個,代碼方面就比較好實現了

可以用排序來進行

但是一看排列,發現取第K大值分為兩部分,且排列都是降序,所以我們可以用兩個數組存儲起來,然後進行賦值(復雜度也比較低)

/*
dp[x][y]表示的是容量為x的第k大值
所以dp[x][1] = max_(第一大值){dp[x][1],dp[x-v][1]+w}
dp[x][2] = max_(第二大值){dp[x][1],dp[x][2],dp[x-v][1]+w,dp[x-v][2]+w}
依次類推~~
*/
/*
 因為dp[j][1]...dp[j][k]與dp[j-w[i]][1]+v[i]...dp[j-w[i]][k]+v[i]
 是依次遞減的,那麽我們可以用兩個數組將這兩組數組保存起來,
 再O(N)的時間內求得第K大。
 */
#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1005;
int dp[maxn][maxn];
int n,W,K;//W:總容量值,K第K大值
int v[maxn],w[maxn];//價值,一個為體積
int s1[maxn],s2[maxn];
void KthZeroOnePack()
{
    for(int i = 0;i < n;i++)//遍歷了每一個物品
    {
        for(int j = W;j >= w[i];j--)//層鋪每一層體積
        {
            for(int th = 1;th <= K;th++)//求取前k大值
            {
                //0 - k-1   到K結束
                s1[th-1] = dp[j][th];//遍歷存儲每一個可能取到的值,且s1是遞減的
                s2[th-1] = dp[j - w[i]][th] + v[i];//遍歷存儲每一個可能取到的值,且s2是遞減的
            }
            //特判結束點
            s1[K] = s2[K] = -1;
            int cnt = 1,cnt1 = 0,cnt2 = 0;
            //從第一大開始
            while(cnt <= K && (s1[cnt1] != -1 || s2[cnt2] != -1))
            {
                if(s1[cnt1] > s2[cnt2])dp[j][cnt] = s1[cnt1++];
                else dp[j][cnt] = s2[cnt2++];
                //嚴格遞減
                if(dp[j][cnt] != dp[j][cnt-1]) cnt++;
            }

        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&W,&K);
        memset(dp,0,sizeof(dp));
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&v[i]);
        }
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&w[i]);
        }
        KthZeroOnePack();
        printf("%d\n",dp[W][K]);
    }
    return 0;
}

第K大01背包