题意:
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
小红正在研究如何把符卡组合出尽可能大威力的组合魔法。
小红共有 n 种符卡可以选择,每种符卡最多只能选择一次,每个符卡的魔力消耗为 ai,威力为 bi。
如果将多个符卡进行组合,则可以发动一个组合魔法。组合魔法的魔力消耗为选择的符卡的魔力消耗的总和,其威力为选择的符卡的威力的总和。
小红必须保证最终符卡的魔力消耗总和为 k 的倍数,否则小红将受到魔力反噬而发动魔法失败。
小红想知道,自己能发动的组合魔法最大的威力是多少?
输入描述:
第一行输入两个正整数 n 和 k ,用空格隔开。
接下来的 n行,每行输入两个正整数 ai 和 bi,用空格隔开。
数据范围:
1≤n,k≤1000
1≤ai,bi≤10^9
输出描述:
如果小红无论如何也组合不了能发动的魔法,则输出-1。 否则输出最大的威力值。
思路:这道题是一个01背包的变形,但要注意初始化的细节,自己再做题时由于初始化的细节没有考虑周全,导致一直过不了题,看了大部分题解是把dp数组初始化为负无穷,这样dp方程很容易给出,我初始化为0导致需要特判某些情况,下次要注意对于01背包初始化为负无穷。
我的代码段
#include<bits/stdc++.h>
using namespace std;
int main(){
long long dp[1010][1010],a[1010],b[1010];
long long n,k;
cin>>n>>k;
memset(dp,0,sizeof dp);//初始化dp数组为0
for(int i=1;i<=n;i++)cin>>a[i]>>b[i],a[i]%=k;//将a[i]取模
for(int i=1;i<=n;i++){
for(int j=0;j<k;j++){
if(dp[i-1][(j-a[i]+k)%k]==0&&j==a[i])dp[i][j]=max(dp[i-1][j],b[i]);
//如果他的前一个为0代表没有途径到达那里,此时只需要判断不选他或者只选他的大小
else if(dp[i-1][(j-a[i]+k)%k]!=0){
dp[i][j]=max(dp[i-1][j],dp[i-1][(j-a[i]+k)%k]+b[i]);
}//正常的dp方程,如果他前一个不为0则有途径到达,直接取选或者不选的最大值。
else if(dp[i-1][(j-a[i]+k)%k]==0)dp[i][j]=dp[i-1][j];
//其他情况下(即没有途径到达且j也不等于a[i])都是不选。
}
}
if(dp[n][0]==0)cout<<"-1"<<endl;
else cout<<dp[n][0]<<endl;
}
题解给出的答案
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[1010][2],dp[1010][1010];
int main(){
int n,k,i,j;
cin>>n>>k;
for(i=1;i<=n;i++)cin>>a[i][0]>>a[i][1];
for(i=0;i<=n;i++)for(j=0;j<=k;j++)dp[i][j]=-1e16;
//注意这里的初始化一定要尽可能小!代表是取不到的。
dp[0][0]=0;
//前0件物品,显然取到的最大威力为0。此时消耗魔力模p等于0
for(i=1;i<=n;i++){
for(j=0;j<k;j++){
dp[i][j]=dp[i-1][j];
//这一部分是不取第i个符卡。
dp[i][j]=max(dp[i][j],dp[i-1][(j-a[i][0]%k+k)%k]+a[i][1]);
//这一部分是取第i个符卡,那么目前的威力如果模k为j的话,一定是从“前i-1个符卡,威力模k为j-a[i]转移而来”
}
}
if(dp[n][0]<=0)cout<<-1;
else cout<<dp[n][0];
}