3.3 A超级肝帝

题目描述

明明迷上了一款叫《超纸片人老婆大乱斗》的游戏。游戏最近新开了一个常驻活动,为了兑换想要的老婆,明明毅然决然的决定化身为肝帝。

活动有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;
} 

猜你喜欢

转载自blog.csdn.net/whisperlzw/article/details/88094202
3.3