P1858 多人背包

题面

01背包的前\(k\)优解

思路

我们先考虑一下最平凡的01背包问题是如何进行求解的,一维状态下

\(f[j]=max(f[j],f[j-w[i]]+v[i])\)

有第k优解的限制怎么办,不妨加一个维度,把动态规划的转移想象成图中点与点之间

的移动

\(f[j][k]\)表示装满体积为\(j\)的背包的第\(k\)优解

我们发现,最优解的数值是随着\(k\)增大而减小的,满足单调性

很容易发现,我们需要记录用其他物品来填充背包是否能得到更优解.

因此我们需要记录一个变量c1表示体积为j的时候的第c1优解能否被更新.

再去记录一个变量c2表示体积为j-v[i]的时候的第c2优解.

这样也就回到了01背包的最基本的情况,选还是不选

显然对于体积为\(j\)的情况,它的前\(k\)优解可以由以上两种情况转移过来

那么我们可以在转移的时候记录一个中间数组,\(now[]\)

所以有以下的代码

int cnt = 0;//对于每个j,我们都需要清空一下now数组
int c1 = 1;//情况1的最优解
int c2 = 1;//情况2的最优解
      while (cnt <= K) {//求出前k优解
        if (dp[j][c1] > dp[j - w[i]][c2] + v[i])
          now[++cnt] = dp[j][c1++];//如果情况1的价值大,取出情况1的价值
        else {
          now[++cnt] = dp[j - w[i]][c2++] + v[i];//如果情况2的价值大,取出情况2的价值
        }
      }
      for (int z = 1; z <= K; z++) {
        dp[j][z] = now[z];//进行更新

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5001;
int dp[5001][51];
int v[N], w[N];
int now[N];
int K, n, m;
long long ans;
int main() {
  scanf("%d%d%d", &K, &m, &n);
  memset(dp, -0x3f, sizeof(dp));
  dp[0][1] = 0;
  for (int i = 1; i <= n; i++) {
    scanf("%d%d", &w[i], &v[i]);
  }
  for (int i = 1; i <= n; i++) {
    for (int j = m; j >= w[i]; j--) {
      int cnt = 0;
      int c1 = 1;
      int c2 = 1;
      while (cnt <= K) {
        if (dp[j][c1] > dp[j - w[i]][c2] + v[i])
          now[++cnt] = dp[j][c1++];
        else {
          now[++cnt] = dp[j - w[i]][c2++] + v[i];
        }
      }
      for (int z = 1; z <= K; z++) {
        dp[j][z] = now[z];
      }
    }
  }
  for (int i = 1; i <= K; i++) ans += dp[m][i];
  printf("%lld\n", ans);
  return 0;
}
posted @ 2020-12-02 08:04  邦的轩辕  阅读(57)  评论(0编辑  收藏  举报