题意
题目描述
求(01)背包前(k)优解的价值和
输入输出格式
输入格式:
第一行三个数(K)、(V)、(N)
接下来每行两个数,表示体积和价值
输出格式:
前(k)优解的价值和
输入输出样例
输入样例#1:
2 10 5
3 12
7 20
2 4
5 6
1 1
输出样例#1:
57
说明
对于(100\%)的数据,(Kleq 50,Vleq 5000,Nleq 200)
思路
让我先秒了这道水题。 --Mercury
多重背包。考场上考到的时候使用的是(STL)中的优先队列求解,即对于每一个(V)状态开一个优先队列,更新时逐个取出再插入,超过(50)个时后面的不用再插回去了。
这是一种显然的蚊子打高射炮高射炮打蚊子的做法,我们不妨这么想:
对于一般的(01)背包问题,我们有标准的状态转移方程(f[i]=max(f[i],f[i-v[j]]+w[j])(igeq v[j]))。也就是说,对于每个物品而言,状态(f[i])只与(f[i])和(f[i-v[j]])有关,所以我们不妨设计(f[i][j])表示(f[i])的第(j)优解,更新时因为(f[i][j])是有单调性的,所以拿单调指针从左往右扫一遍,扫出(k)个最优解,来更新就好了:
for(int i=V;i>=v[j];i--)//01背包倒序枚举
{
int cnt=0,cfr=0,cto=0;//cnt记录更新后的解,cto记录更新前的解,cfr记录f[i-v[j]]的解
while(cnt<k)//找到k个解
if(f[i][cto]>f[i-v[j]][cfr]+w[j]) tmp[cnt++]=f[i][cto++];//扫描
else tmp[cnt++]=f[i-v[j]][cfr++]+w[j];
for(int w=0;w<k;w++) f[i][w]=tmp[w];//更新
}
AC代码
#include<bits/stdc++.h>
using namespace std;
int k,v,n,ans,f[5005][55],tmp[55];
int read()
{
int re=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
int main()
{
k=read(),v=read(),n=read();
memset(f,0xcf,sizeof f);
f[0][0]=0;
while(n--)
{
int x=read(),y=read();
for(int i=v;i>=x;i--)
{
int cnt=0,cfr=0,cto=0;
while(cnt<k)
if(f[i][cto]>f[i-x][cfr]+y) tmp[cnt++]=f[i][cto++];
else tmp[cnt++]=f[i-x][cfr++]+y;
for(int j=0;j<k;j++) f[i][j]=tmp[j];
}
}
for(int i=0;i<k;i++) ans+=f[v][i];
printf("%d",ans);
return 0;
}