[洛谷P1858] 多人背包

时间:2021-02-15 07:28:26

洛谷题目链接:多人背包

题目描述

求01背包前k优解的价值和

输入输出格式

输入格式:

第一行三个数K、V、N

接下来每行两个数,表示体积和价值

输出格式:

前k优解的价值和

输入输出样例

输入样例#1:

2 10 5

3 12

7 20

2 4

5 6

1 1

输出样例#1:

57

说明

对于100%的数据, \(K\le 50,V\le 5000,N\le 200\)

题意已经很清楚了,就不多赘述了.

题解:

首先考虑一下如何做01背包.显然有$$f[j]=max(f[j], f[j-cost[i]]+value[i])$$.那么我们应该如何记录这个前\(k\)优的解呢?

首先是应该想到将前\(k\)优的解加入状态的转移中.先定义状态\(f[j][k]\)表示\(j\)的容量的第\(k\)优解的值.考虑一下转移的情况,显然\(f[j][k]\)的情况只能由\(f[j][k]\)和\(f[j-cost[i]][1...k]\)转移而来(其实这个\(1...k\)是一个确定的值,因为\(k\)越大,\(f[j-cost[i]][1...k]\)越小,也就是说这个是单调的.肯定只有一个值能转移到\(f[j][k]\)的状态).

那么既然这个是单调的,并且又只有两种决策,那么其实这里是可以用归并来求解最大值的.在枚举的时候,可以用两个指针记录已经转移到第几个状态.每次选择大的那一个,最后归并回原数组(这里我是一边枚举一边归并的).

#include<bits/stdc++.h>
using namespace std;
const int V=5000+5;
const int N=200+5;
const int K=50+5; int n, v, k, c[N], w[N], q[K], ans = 0;
int f[V][K]; int main(){
cin >> k >> v >> n;
memset(f, 128, sizeof(f)); f[0][1] = 0;
for(int i=1;i<=n;i++) cin >> c[i] >> w[i];
for(int i=1;i<=n;i++)
for(int j=v;j>=c[i];j--){
int now = 1, last = 1, cnt = 0;
while(cnt < k){
if(f[j][now] > f[j-c[i]][last]+w[i])
q[++cnt] = f[j][now++];
else q[++cnt] = f[j-c[i]][last++]+w[i];
}
for(int o=1;o<=k;o++) f[j][o] = q[o];
}
for(int i=1;i<=k;i++) ans += f[v][i];
printf("%d\n",ans);
return 0;
}