动态规划解决愤怒的小鸟问题

愤怒的小鸟

一、题目描述

游戏"愤怒的小鸟",各位仙家都玩过了吧,俗话说:“飞得越高,砸得更狠”,各小鸟们在游戏中简直是各显神通,竞相飞得更高,哪怕是粉身碎骨,但求美名留人间。
为了获得更高的飞越高度,小鸟们不知从何处得到了一批神力大补丸,吃了这些大补丸将可以帮助小鸟们获得更高的飞行高度。不幸的是,不知道那个该死的叛徒走漏了消息,可恶的绿猪获得了这个情报,于是他们贿赂了当地的一个巫师,希望巫师从中作梗为难小鸟们,于是巫师连夜在这批大补丸上施加了一种可怕的诅咒,就是小鸟们在服用这些大补丸时,当吃到到奇数个大补丸会增加飞行功力(增加值为该大补丸的“药力”值);吃到偶数个大补丸会降低飞行功力(降低值也是该大补丸的“药力”值),当然小鸟们在挑选时可以跳过某些大补丸不吃。小鸟们有点犯难,虽然说只要谨慎挑选是可以确保吃了这批大补丸后能得到功力的提升,但神力大补丸之所以称为神力,那可是花了高价钱好不容易才搞到手的,怎么可以随便处理了事。有没有一种选择方案可以让这批神力大补丸发挥最大的药效呢?
幸好,小鸟们有个当程序员的朋友,也就是你,他们现在求助于你,那么你能找到一种选择方案可以让这批神力大补丸发挥最大的药效吗?
输入:
输入有多组用例,对每组用例:
第1行:一个整数n(1<=n<=150,000),表示大补丸的数量
第2行:包含n个整数pi(1<=pi<=500),每个整数表示一个大补丸的“药力”值。假设小鸟们只能按这个给定的次序依次挑选大补丸。
输出:
第1行:一个整数,食用这批神力大补丸后能达到的最大的药效。
输入样例:
8
7 2 1 8 4 3 5 6
12
51 141 41 152 79 72 28 145 41 26 176 78
输出样例:
17
519

二、【问题求解】

1.提示:
样例解释:第1个样例,依次取7,1,8,3,6,最大的药效=7-1+8-3+6=17;第2个样例,依次取141,41,152,28,145,26,176,最大的药效=141-41+152-28 +145-26+176=519。

2.思路
可分别标记处理完第i个大补丸后,当前拿到的是偶数个大补丸或者是奇数个大补丸的最优值,再考虑上一个状态如何到达这个状态。最终解为处理完最后一个大补丸后,当前是偶数个大补丸的最优值和当前是奇数个大补丸的最优值的较大值。

3.算法设计:
在本题中需要求出小鸟们食用这批神力大补丸后能达到的最大的药效。当小鸟吃到到奇数个大补丸会增加飞行功力,吃到偶数个大补丸会降低飞行功力(降低值也是该大补丸的“药力”值),这个题目用动态规划方法解决,对拿到奇数个和偶数个大补丸的小鸟分别考虑。

4.主要变量说明:
n表示大补丸的数量,a[i]表示各大补丸的药力值,dp[i]为动态规划数组,表示有i个小鸟挑选药丸时候药力的最大值,count用于计数,s用于计算总的药力值。

5.代码实现:

#include<stdio.h>
int n;  //表示大补丸的数量 
int a[150000];  //各个大补丸的药力值 
int dp[150000];  //动态规划数组表示有i个小鸟挑选药丸时药力的最大值 
int GetIndex_1(int j){  //返回数组后面中值最大的下标 
	int i;
	for(i=j;i<n-1;i++){
		if(a[i]>a[i+1])
		return i;
	}
	return i;
}
int GetIndex_2(int j){   //返回数组后面中值最小的下标 
	int i;
	for(i=j;i<n-1;i++){
		if(a[i]<a[i+1])
		return i;
	} 
	return i;
}
int main(){
	int count;
	while(scanf("%d",&n)!=EOF){
		count=1;  //用于计数 
		int s=0;  //用于计算总的药力值
		for(int i=0;i<n;i++){
			scanf("%d",&a[i]);
		} 
		for(int j=0;j<n-1;j++){
			if(count%2!=0) //偶数个选取药丸
			{
				if(a[j]>a[j+1]){
					
					s+=a[j];
				}
				else{
					j=GetIndex_1(j);
					s+=a[j];
				}
			 } 
			 else{ //奇数个选取药丸 
			 		if(a[j]<a[j+1]){
					
					s-=a[j];
				}
				else{
					j=GetIndex_2(j);
					s-=a[j];
			 }
		}
		dp[count]=s;
		count++;
	}
	//此处为冒泡排序,用于得到dp数组中的最大值
	//(对冒泡排序不熟悉的可参考排序专栏中的冒泡排序
	for(int k=1;k<=count;k++){
		for(int p=1;p<=count-k;p++){
		if(dp[k]>dp[k+1])
		{
			int t=dp[k+1];
			dp[k+1]=dp[k];
			dp[k]=t;
		}
	}
	}
	printf("%d\n",dp[count]);
}
	return 0;
} 

此处参考来自
@给予我灵感的帅气善良体贴有趣好评十分不够的hxl小哥哥(手动艾特)

冒泡排序博文传送门:https://blog.csdn.net/weixin_44279771/article/details/105586116

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值