拼题 A 的教超搞打卡活动,指定了 N 张打卡卷,第 i 张打卡卷需要 mi 分钟做完,完成后可获得 ci 枚奖励的金币。活动规定每张打卡卷最多只能做一次,并且不允许提前交卷。活动总时长为 M 分钟。请你算出最多可以赢得多少枚金币?
输入格式:
输入首先在第一行中给出两个正整数 N(≤103) 和 M(≤365×24×60),分别对应打卡卷的数量和以“分钟”为单位的活动总时长(不超过一年)。随后一行给出 N 张打卡卷要花费的时间 mi(≤600),最后一行给出 N 张打卡卷对应的奖励金币数量 ci(≤30)。上述均为正整数,一行内的数字以空格分隔。
输出格式:
在一行中输出最多可以赢得的金币数量。
输入样例:
5 110
70 10 20 50 60
28 1 6 18 22
输出样例:
40
样例解释:
选择最后两张卷子,可以在 50+60=110 分钟内获得 18+22=40 枚金币。
做法
01背包问题。
dp数组第一维是考虑了前i个卷子,第二维是花费的时间。
#include<bits/stdc++.h>
using namespace std;
int n,m;
int ans=-0x3f3f3f3f;
int a[1010],b[1010];
int dp[1010][600000];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
memset(dp,-0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=n;i++){//考虑前i个
for(int j=0;j<=m;j++){
if(j>=a[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-a[i]]+b[i]);
dp[i][j]=max(dp[i][j],dp[i-1][j]);//别忘了更新当前的
}
}
for(int i=0;i<=m;i++) ans=max(ans,dp[n][i]);
cout<<ans;
}
但是吧,dp数组超空间了,得改成1维数组。
#include<bits/stdc++.h>
using namespace std;
int n,m;
int ans=-0x3f3f3f3f;
int a[1010],b[1010];
int dp[600000];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
memset(dp,-0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--){//倒序
if(j>=a[i]) dp[j]=max(dp[j],dp[j-a[i]]+b[i]);
}
}
for(int i=0;i<=m;i++) ans=max(ans,dp[i]);
cout<<ans;
}
这么交上去结果运行超时了,有几个的过不去。为什么呢,因为我们的m太大了。那我们就把dp数组的下标表示为金币,而不是时间。注意dp数组初始化的值
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[1010],b[1010];
int dp[30010];
int mv;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]),mv+=b[i];
memset(dp,0x3f,sizeof(dp));//初始化的值不同
dp[0]=0;
for(int i=1;i<=n;i++){
for(int j=mv;j>=0;j--){
if(j>=b[i]) dp[j]=min(dp[j],dp[j-b[i]]+a[i]);//取最小值,因为取得相同金币,时间越少越好
}
}
for(int j=mv;j>=0;j--){
if(dp[j]<=m){
cout<<j;
return 0;
}
}
}