http://acm.hdu.edu.cn/showproblem.php?pid=2639
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
12 2 0
题目大意:
见之前的收集骨头的博客,题意类似,给定背包容量,骨头的个数和每个骨头的价值,这次不是求在背包容量允许的情况下,最多装的价值,而是求在背包容量内,可以装的第k大价值,如果没有第k个最大值,那么输出0
输入包括多组样例,第一行输入一个T,样例的个数,接下来每个样例都有三行,第一行包括三个整数,N,V,K,分别代表骨头的个数,背包的容量,我们需要输出的第K个最大值,第二行包括N个数,分别代表骨头的数量和接下来一行有N个数,分别表示每种骨头的价值。
输出第K个最大价值,每个样例输出一行
思路:简单的01背包基础上做,要求的是第K个最大值,那么不用dp[j]=max(dp[j],dp[j-w[i]]+v[i])的状态转移方程,而是将两个值都记录下来,用for循环走一遍,记录下,容量为1到M的各个最大价值,dp[i][j]表示当背包容量为i时的第j个最大价值,最后只需要输出dp[m][k]即可
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cmath> #include <cstdlib> #include <limits> #include <queue> #include <stack> #include <vector> #include <map> using namespace std; typedef long long LL; #define N 1100 #define INF 0x3f3f3f3f #define PI acos (-1.0) #define EPS 1e-8 #define met(a, b) memset (a, b, sizeof (a)) int dp[N][N]; int main () { int t, n, m, K, v[N], w[N], a[N], b[N]; scanf ("%d", &t); while (t--) { scanf ("%d %d %d", &n, &m, &K); for (int i=1; i<=n; i++) scanf ("%d", &v[i]); for (int i=1; i<=n; i++) scanf ("%d", &w[i]); met (dp, 0); met (a, 0); met (b, 0); for (int i=1; i<=n; i++) for (int j=m; j>=w[i]; j--) { for (int k=1; k<=K; k++) { a[k] = dp[j][k]; b[k] = dp[j-w[i]][k] + v[i]; } a[K+1] = b[K+1] = -1; int x = 1, y = 1, z = 1; while ((a[x] != -1 || b[y] != -1) && z <= K) { if (a[x] > b[y]) dp[j][z] = a[x++]; else dp[j][z] = b[y++]; if (dp[j][z-1] != dp[j][z]) z++; } } printf ("%d\n", dp[m][K]); } return 0; }