题目描述
约翰的干草库存已经告罄,他打算为奶牛们采购H(1≤H≤50000)磅干草,他知道N(1≤N≤100)个干草公司,现在用1到N给它们编号。第i个公司卖的干草包重量为Pi(1≤Pi≤5000)磅,需要的开销为Ci(l≤Ci≤5000)美元.每个干草公司的货源都十分充足,可以卖出无限多的干草包.帮助约翰找到最小的开销来满足需要,即采购到至少H磅干草.
输入格式
第1行输入N和H,之后N行每行输入一个Pi和Ci.
输出格式
最小的开销.
与普通的完全背包不同的是,这题装的干草可以超过所需的重量。这里介绍两种常见的做法。
1.把背包总体积往上扩一段,也就是把背包容量扩大。设此时的容量为m'。然后做完全背包即可。最后枚举m~m'的每一位,取最小代价。由于题中的Pi≤5000,所以让m'等于m+5000即可。
2.刷表法。设当前已经买了j磅的干草,已经买了i-1种干草,每种干草的重量为ci、价格为vi。那么:
[dp[j+c[i]]=Min(dp[j+c[i]],dp[j]+v[i])
]
如果j+c[i]>m,那么直接计算到dp[m]的头上即可。所以把算式微调一下,改为:
[dp[Min(j+c[i],m)]=Min(dp[Min(j+c[i]),m],dp[j]+v[i])
]
第一种的时间复杂度为O(N(M+Max{ci}));第二种为O(NM)。实际上跑得差不多快。
第二种的代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 101
#define maxm 50001
using namespace std;
int dp[maxm],c[maxn],v[maxn];
int n,m;
inline int read(){
register int x(0),f(1); register char c(getchar());
while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int main(){
n=read(),m=read();
for(register int i=1;i<=n;i++) c[i]=read(),v[i]=read();
memset(dp,0x3f,sizeof dp);
dp[0]=0;
for(register int i=1;i<=n;i++){
for(register int j=0;j<=m;j++){
dp[min(j+c[i],m)]=min(dp[min(j+c[i],m)],dp[j]+v[i]);
}
}
printf("%d
",dp[m]);
return 0;
}