程序设计训练(四)

在假期结束之前,总结一下假期的收获——动态规划。
其中会简单和分治法进行比较。

动态规划

让我们先看一下百度百科上对动态规划的定义:

动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法,它把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。

按照我的理解,也就是把大事化小,小事化无,把问题划分成我们方便处理的状态,也就是dp问题
dp中的几个常见概念:

  • 阶段。把问题分成几个相互联系的有顺序的几个环节。
  • 状态。某一阶段的出发位置。通常一个阶段包含若干状态。
  • 决策。从某阶段的一个状态演变到下一个状态的选择。
  • 策略。有起点到终点的全过程中,由每个决策组成的决策序列。
  • 状态转移方程。由i阶段到i+1阶段状态的演变规律。

dp适用的基本条件:

  • 具有相同子问题。一个问题能够分解出几个子问题,而这些子问题也能分解为新的相同的子问题,即一个问题能被分解为A、B、C,那么A、B、C分别也能分解为A、B、C,却不能分解为D、E、F。
  • 满足最优子结构。问题的最优解包含着它的子问题的最优解。即不管前面的策略如何,此后的决策必须是基于当前状态(由上一次决策产生)的最优决策。
  • 满足无后效性。“过去的步骤只能通过当前状态影响未来的发展,当前的状态是历史的总结”,也就是当前问题的决策不能对后来问题的决策产生影响,状态出现在策略任何一个位置,它的地位相同,都可实施同样策略。

解决dp问题的核心:构造递推式,也可以说是状态转移方程,如F(n) = F(n-1) + F(n-2)
个人感觉这是整个dp问题最难的地方。
解决dp问题的核心方法:自底向上(递推)或者自顶向下(记忆化搜索)。
为了减少灌水,这里贴两篇文章,一篇很好的说明了解决dp问题的核心方法,一篇形象的介绍了dp,良心推荐。

和分治法的比较

分治,“分而治之”,也就是把一个难以直接解决的大问题,分割成一些规模比较小的相同的小问题,最后合并结果,划分为分解—>解决—>合并三个步骤。

分治法是将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解。而动态规划适用于子问题独立且重叠的情况,也就是各子问题包含公共的子 子问题。

具体实现

常用解决dp问题步骤:

  1. 找到一个原问题,并分析它的子问题。量化问题;
  2. 根据原问题和子问题确定状态。可以根据问题中的变量来判断有几个状态;
  3. 确定状态转移方程。额外注意一下临界的特殊条件;
  4. 确定编程实现方式。看是选择从上到下还是从下到上。

以最常见的背包问题为例。

有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

分析问题可以得出,其中的变量是物品编号i和背包剩余容量j(常量是物品的价值w和总容量V);
确定状态f[i][j],表示前i件物品放入一个容量为j的背包可以获得的最大价值 ;
确定状态转移方程,由i-1的结果判断放不放i,放的话f[i][j] = f[i-1][j-v[i]]+w[i],不放的话f[i][j] = f[i-1][j],最后可以得出f[i][j] = max{f[i-1][j], f[i-1][j – v[i]] +w[i]}
最后编程实现:

#include<iostream>
#include<algorithm>

#define maxSize 10001

using namespace std;

int main(){
	int n,v,i,j;
	int vol[maxSize],val[maxSize],f[maxSize];

	memset(f,0,sizeof(f));
	cin>>n>>v;
	for(i = 0;i < n;i++){
		cin>>vol[i]>>val[i];
	}

	for(i = 0;i < n;i++){
		for(j = v;j >= vol[i];j--){
			if(f[j-vol[i]]+val[i] > f[j]){
				f[j] = f[j-vol[i]] + val[i];
			}
		}
	}

	cout<<f[v]<<endl;

	//system("pause");
	return 0;
}

时间复杂度O(VN),空间复杂度O(VN) 。
更多解看这里,有问题会再修改的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值