中文题,不多说。完全背包问题。
之前刚刚敲了01背包的题目,趁热做了完全背包问题。一拿到这道题可以直接看出是完全背包,但是确定不好如何套入完全背包模型中,只看出经验值一定看成物品价值。然后仔细想了一下,题目中总共有三个变量,经验值、杀怪数以及忍耐值,明显经验值看成物品价值,然后就剩下背包容量和物品数量。本来是想把杀怪数当作物品数量、忍耐值当作背包容量的,这样的话程序怎么设计怎么不舒服。于是度娘,看到某博主是反过来写的,想了一下还确实方便了不少然后参考了一下他的代码。只是参考了一下哦,真的只是参考了一下哦~~实现方法就是,数据量不大,按照最基本的完全背包问题解法来解,先把忍耐值i从1开始循环,内嵌怪物种类kind从头到尾开始扫,每扫一种怪物就内嵌一个杀怪数v从1到最大的循环,循环中再内嵌该种类杀死怪物数量K的循环,K需要满足K * mon[kind][1] <= i&& K <= v,然后计算状态转移方程FT[i][v]=MAX(FT[i][v],FT[i-K*mon[kind][1]][v-K]+K*mon[kind][0])。一步步循环下来,每一次i的内部循环结束,就判断一下FT[i][s]是否大于等于要求的经验值n,至于取FT[i][s]是因为每次内部循环结束,FT[i][s]必然是FT[i][1~s]中最大的,因为它比其他的杀怪数都多,相应的经验值也最多,而所有的忍耐值都是相同的,题目中只要求最大剩余忍耐值,对杀怪数只有不大于的要求,所以取这个。一旦满足FT[i][s] >= n就break,这样得到的i就是最小的。所有循环结束后判断i,如果i>m,说明循环没有跳出,即无法在要求条件内升级,于是输出-1,否则就输出m-i。写了这么多,收获就是背包问题重在找到变量并对应到背包模型中,如果写起来不顺手就换一种思路想一下。还有,变量太多就标记一下,加点注释,不然写到后来自己都忘记了前面的设定了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std ;
const int MAXN = 105 ;
int mon[MAXN][2] = {0} ; ///怪物的经验、扣除的忍耐度
int FT[MAXN][MAXN] = {0} ; ///当前的经验值
int MAX(int x , int y) {
return (x > y)? x : y ;
}
int main() { /// n 还需的经验值
//freopen("in.txt" , "r" , stdin) ; /// m 保留的忍耐度
int n , m , k , s ; /// k 怪的种数
while (cin >> n >> m >> k >> s) { /// s 最多的杀怪数
memset(FT , 0 , sizeof(FT)) ;
for (int i = 0 ; i < k ; i ++) {
cin >> mon[i][0] >> mon[i][1] ;
}
int i , kind , v ;
for (i = 1 ; i <= m ; i ++) { ///忍耐度
for (kind = 0 ; kind < k ; kind ++) { ///怪物种类
for (v = 0 ; v <= s ; v ++) { ///杀怪数
int K = 1 ; ///怪物量
while (K * mon[kind][1] <= i && K <= v) {
FT[i][v] = MAX(FT[i][v] , FT[i-K*mon[kind][1]][v-K] + K*mon[kind][0]) ;
K ++ ;
}
}
}
if (FT[i][s] >= n) break ;
}
if (i > m) cout << "-1\n" ;
else cout << m - i << endl ;
}
return 0 ;
}