题目描述
尤娜进入了幻想世界。这个世界的神给了她一套强大的装备,使她能够击败许多凶猛的怪物。然而,她的生命值和耐力值有限,无法杀死大量的怪物。
冒险家公会给出n个杀死怪物的任务,完成第i个任务会消耗尤娜hi点生命值和si点耐力值,然后她就会得到wi个金币。
一开始,尤娜有H点生命值和S点耐力值。当她的生命值降到低于或等于0,她会死的。然而,当她的耐力值下降到0,她不肯放弃,透支的耐力会从生命值上减少。例如,当她的耐力值为-3时,生命值会下降3点,然后她的耐力值会重置为0。如果她的生命值不能承受透支的耐力,她也会死。
作为尤娜的朋友,你能帮她选择一些任务来完成以获得最大数量的金币吗?千万别让尤娜死,否则你会很伤心的。
输入描述
第一行包含三个整数n,H,S(1<=n<=1000,1<=H<=300,0<=S<=300)
接下来的n行描述杀死怪物的任务,第i行包含三个整数hi,si,wi(0<=hi,si<=300,1<=wi<=10^9)
输出描述
输出一个整数:尤娜可以得到的金币最大值。
题目分析
训练时好歹知道这是个背包题,第一次自己写背包,还是不太会写,理解的不够深!!(/ω\)
可以看出这是一个二维费用背包问题,唯一不同的地方就是当耐力值减少到0和生命值减少到0的时候需要特判,其他和二维费用背包没有区别。但是注意要把三维状态转移方程压缩为二维,否则会内存超限,然后在遍历生命值和耐力值的时候倒序遍历。
#include<bits/stdc++.h>
using namespace std;
int h[1005],s[1005];
long long dp[1005][1005];//10^9最好开一个long long数组
long long w[1005];
int main(){
int n,hm,sm,i,j,k;
cin>>n>>hm>>sm;
for(i=1;i<=n;i++){
cin>>h[i]>>s[i]>>w[i];
}//默认dp数组里初始都是0,想了想也没有其他需要特殊赋初值的地方,所以没有赋初值的步骤
for(i=1;i<=n;i++){
for(j=hm;j>0;j--){//倒序遍历生命值,表示最大生命为j时在前i个任务里选择的最大值
for(k=sm;k>=0;k--){//倒序遍历耐力值,表示最大耐久为k时在前i个任务里选择的最大值
if(j>h[i]&&j+k>s[i]+h[i]&&k<s[i]){//最大生命j大于第i个任务的生命值,最大耐力值k小于第i个任务的耐力值,且生命和耐力值之和大于第i个任务的生命和耐力值之和
int res=s[i]-k;//求出耐力值减为0后需要在生命值里多减去的耐力值
dp[j][k]=max(dp[j][k],dp[j-h[i]-res][0]+w[i]);//注意这里耐力值为0
}
else if(k>=s[i]&&j>h[i]){//最大生命值j大于第i个任务生命值且最大耐力值k大于第i个任务耐力值,正常dp就行
dp[j][k]=max(dp[j][k],dp[j-h[i]][k-s[i]]+w[i]);
}
}
}
}
cout<<dp[hm][sm]<<endl;//输出最大生命值和耐力值为题目给定值的dp结果
return 0;
}