纠结的分析过程——01背包

该题代码中包含具体分析,相信和我一样纠结的感觉01背包逻辑顺不通的人能够有所收获。
8615 快乐
该题有题解

时间限制:500MS 代码长度限制:10KB
提交次数:312 通过次数:98

题型: 编程题 语言: G++;GCC
Description
Lian是一个喜欢看动画片的人,自从成为ACMer(ACM爱好者)之后,他又迷上了网上做题。做题让他快乐,不过这也是需要付出精力的!!
假设有n道题,Lian做出第i道题后,他可以获得的快乐指数将增加gethappy[i],而消耗掉的精力将是losspow[i]。
假设Lian初始的快乐指数为1,精力为2000。可以理解,如果他消耗完了所有的精力那他得到再多的快乐都没有用。
你的任务就是帮他计算他所能得到的最多的快乐指数,且最后他依然有多余的精力(即至少为1)。

输入格式
第一行输入一个整数n,表示有n道题。(n<=50)
第二行输入n个整数,表示gethappy[1]到gethappy[n]
第三行输入n个整数,表示losspow[1]到losspow[n]。

输出格式
一个整数,表示Lian所能获得的最大快乐指数。

输入样例
3
15 23 61
350 1301 1513

输出样例
77

#include<cstdio>
#include<cstring>
/*
简单01背包问题,我就不数组压缩了,但会将过程详细写出,帮助有和我一样疑惑的同学 
*/
/*
1:动态规划就是记忆化的递归,它实现了对子问题的保存,从而减少重复计算,加快效率。 

2:动态规划要求有无后效性和最优子结构,这个要大家慢慢体会。

3:动态规划的状态实际上就是对问题的合理分解,状态的值就是那个状态的解。

4:状态实际上跟问题的分解有关,问题的分解不一定要跟题目的提问一样,有的时候要适当变化一下,但用新的状态最终能够反映问题的解。
	状态设的好,有的时候对问题的解决就更加容易。
	
5:在满足上面几点条件的时候,就可以开始想动态规划了,
一般步骤:1:分析分解问题,提出合理的状态,一般未来用数组保存。
		  2;找到初始状态和状态转移方程,实际上就是地图式。
		  3:求解,一般有记忆型递归法和递推法,求解的原理是,初始状态实际上就是边界条件可以较易得到。切记:初始状态可能不只有1个。
		  4:根据状态转移方程和初始状态一层层求解,进而求出全部的状态。注意,状态一定要保存下来,这样才能够真正的节省时间。 
*/ 
/*
这一题,我就参照这个01背包题目讲一下思路过程,并讲一下01背包中易错的地方。 
*/
int dp[60][3000];     //状态保存,具体见下。 
int gethappy[60];     //每个的开心值。 
int losspow[60];	  //每个的失去精力值。 
int max_happy(int a,int b){
	return a>b?a:b;
}
int main()
{
	/*
	问题分解:做前i道题,使得他的精力消耗不大于j的最大快乐数。
	 
	 初始状态:做前1道题,使得它的精力不超过j。注意,这个j不是固定的,因为原则上每个题目都是平等的,
	 			到这个题目的时候,到底还有多少精力,到达做不做这道题目是不固定的,我们只是用了合理的状态伪分配了顺序。
				 所以实际上,对于每个问题,我们要把精力从0枚举到2000。说精力还有1点对吗,我们最后再小处理一下就好。
				 
				做前1道题目,如果精力大于做这道题目所消耗的,就一定做它。因为就一道题目呀!我上面虽然说了题目是平等的,
				但对于你设计这个状态,你就要看成是只有1件物品。而问题的所有解,实际上是设计的有的没的状态整合起来的。(不要管这句话。) 
				(还有一种看法,这是最后一件物品,但也会有你不能完全说通的地方)
	*/
	//初始状态
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&gethappy[i]);
	for(int i=1;i<=n;i++)scanf("%d",&losspow[i]);
	//精力从0枚举到2000 
	for(int j=0;j<=2000;j++){
		//如果可以写题就一定写 
		if(j>=losspow[1]){
			dp[1][j]=gethappy[1];
		}else{
			dp[1][j]=0;
		} 
		//如果不能写题就不写 
	} 
	//状态转移,对于后面的前n道题中,第n道题中你可以写,也可以不写,因为顺序实际上也是随意的,你可能要留精力写前面的题,但你总是渴望最优的。
	//实际上,你可能会感觉逻辑有的时候有点乱,但就是这样,你这状态能求出解,就要用些新奇逻辑,对动态规划尤为重要。 
	//前2到前n道题,层层递推 
	for(int i=2;i<=n;i++){
		//你确实还不知道体力是多少,因为选择多样 
		for(int j=0;j<=2000;j++){
			if(j<losspow[i])dp[i][j]=dp[i-1][j]; 
			else{
				//是否选第i道题 
				dp[i][j]=max_happy(dp[i-1][j],dp[i-1][j-losspow[i]]+gethappy[i]);
			}
		}
	}
	printf("%d\n",dp[n][1999]+1);//加上初始快乐点,设精力为1999,及保留1点精力 
	/*
	上面分析了这么多,我突然怀疑我状态分析错了。我感觉,这个状态转移是这样的:
	实际上,逻辑上是从最后一个物品开始往前选的,但只有一件物品的时候是边界状态。所以从边界状态往往后分析。当然你不知道还有多少体力 
	另一个证明缘由是,dp[i][j]与dp[i-1][j-losspow[i]]对应,就是说前面物品跟更少的精力相对应,所以逻辑顺序应该是从后往前。
	我说了这么多别打我,相信你还是有所收获的! 
	*/ 
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值