Description
Input
Output
Sample Input
3 5 10 2 1 2 3 4 5 5 4 3 2 1 5 10 12 1 2 3 4 5 5 4 3 2 1 5 10 16 1 2 3 4 5 5 4 3 2 1
Sample Output
12 2 0
思路:求第K大的最优值,最普通的01背包我们都只保存子问题唯一的最优值,现在则需要保存子问题的前K大的最优值,
假设在普通的01背包中,dp[i]即表示体积 i 的最优值(二维dp可优化成一维dp,详见背包九讲),dp[i][k]表示体积为 i 时的第K个最优值,那么一开始dp[i][1...k]均应初始化为0
01背包中有状态转移方程:dp[i]=max(dp[i],dp[i-v]+v),事实上就是从一个最优值衍生多一个"最优值"后,再在二者中选择最优的保留,其实此时可看做本题的k为1的情况.
所以求第K大,只需要每次保留前K个最优值,再衍生出K个“最优值”,从这2K个“最优值”中选出前K个不重复的最优值,反复如此即可。
可能表述不清,还望指出.....
AC代码(含注释):
#include<iostream> #include<string.h> using namespace std; typedef long long LL; #define min(a,b) (a>b?b:a) #define max(a,b) (a>b?a:b) #define init(a,num) memset(a,num,sizeof(a)) int a[1005],b[1005]; int dp[1005][35]; int main() { int t; cin >> t; while (t--) { int n, v, k; cin >> n >> v >> k; for (int i = 0; i < n; ++i) { cin >> b[i]; } for (int i = 0; i < n; ++i) { cin >> a[i]; } int d1[35],d2[35]; init(dp,0); init(d1,0); init(d2,0); for (int j = 0; j < n; ++j) { for (int i = v; i >= a[j]; --i) { int l; for(l=1;l<=k;++l) { d1[l]=dp[i][l];//上个子问题中保留的前K个最优值 d2[l]=dp[i-a[j]][l]+b[j];//由K个最优衍生出来的K个值 } d1[l]=d2[l]=-1; int pos,pos1,pos2; pos=pos1=pos2=1; while(pos<=k&&(d1[pos1]!=-1||d2[pos2]!=-1))//合二为一 { if(d1[pos1]>d2[pos2]) { dp[i][pos]=d1[pos1++]; } else { dp[i][pos]=d2[pos2++]; } if(dp[i][pos-1]!=dp[i][pos])pos++;//关键,去重 } } } cout << dp[v][k] << endl; } return 0; }