题目描述
明明迷上了一款叫《超纸片人老婆大乱斗》的游戏。游戏最近新开了一个常驻活动,为了兑换想要的老婆,明明毅然决然的决定化身为肝帝。
活动有10个关卡,每次从第一关开始连续闯关,结束闯关后按照闯过的关卡数扣除体力并获得积分。活动规定连闯K关获得K积分,但连闯不同数目的关卡需要消耗的体力是不一样的,例如:
关卡数 1 2 3 4 5 6 7 8 9 10
体力 12 21 31 40 49 58 69 79 90 101
因为一共只有10个关卡,所以一次闯关最多获得10个积分。明明的老婆需要n积分才能兑换,而他可以通过氪金买体力来进行任意次数的闯关。请你帮他找到一种闯关方案,使得最后消耗的体力最少
注意:连闯10关的体力消耗比只闯一关的体力消耗小的情况是允许的。
输入
共两行
第一行为10个整数,依次表示连续闯1~10关的体力消费
第二行为明明要兑换的老婆需要的积分。
输出
一行,一个整数,代表兑换到明明想要的老婆需要的体力总额。
样例输入
12 21 31 40 49 58 69 79 90 101
15
样例输出
147
解题思路
题目输入的是,连续闯i关所消耗的体力。
方法一:用dp[i]表示获得i积分所消耗的最少体力,获得i积分的状态可以由,获得了i - j积分之后,再连续闯过j关,转移过来。由此得到状态转移方程dp[i] = min(dp[i - j] + v[j]),v[j]为连续闯过j关所消耗的体力,j<=i && j <= 10。
方法二:以需要的积分作为背包容量,闯关所获得的积分作为物品的体积,闯关所消耗的体力作为物品的价值。和一般背包不同的是,这题求的是最小价值。所以初始化的时候不能把dp数组初始化为0,而是初始化为无限大,dp[0]为0。然后算完全背包。完全背包就是所有物品可以取无数次,代码压维了。
方法一代码如下
#include <iostream>
#include <cstring>
#define maxn 1000005
using namespace std;
int dp[maxn]; //获得i积分(闯过i关)所需最小体力
int v[maxn]; //连闯i关所需体里
int main()
{
memset(dp, 0x7f, sizeof(dp)); //求最小体力,所以先设为INF
for(int i = 1; i <= 10; i ++)
cin >> v[i];
int n;
cin >> n;
dp[0] = 0; //0积分只要0体力
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= 10 && j <= i; j ++){ //闯过i关可以由i-j关,再闯j关得到
dp[i] = min(dp[i], dp[i - j] + v[j]); //取其中最小的
}
}
cout << dp[n] << endl; //输出闯过n关,即得到n积分的最小体力
return 0;
}
方法二代码如下:
#include <iostream>
#include <cstring>
#include <cmath>
#define maxn 1000005
using namespace std;
int dp[maxn];
int w[15]; //体力值
int v[15]; //积分
int main()
{
memset(dp, 0x7f, sizeof(dp));
dp[0] = 0;
for(int i = 1; i <= 10; i ++){
cin >> w[i];
v[i] = i;
}
int n;
cin >> n;
for(int i = 1; i <= 10; i ++){ //完全背包
for(int j = v[i]; j <= n; j ++)
dp[j] = min(dp[j], dp[j - v[i]] + w[i]);
}
cout << dp[n] << endl;
return 0;
}