洛谷题目链接:多人背包
题目描述
求01背包前k优解的价值和
输入输出格式
输入格式:
第一行三个数K、V、N
接下来每行两个数,表示体积和价值
输出格式:
前k优解的价值和
输入输出样例
输入样例#1:
2 10 5
3 12
7 20
2 4
5 6
1 1
输出样例#1:
57
说明
对于100%的数据, (Kle 50,Vle 5000,Nle 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
",ans);
return 0;
}