动态规划初步

基本原理

类似于递归解题,把问题丢给上一层来解决,找出状态转换方程即可。当然关键问题是如何定义递归数组与找出状态转换方程。

Hello World

走楼梯:有n阶楼梯,每次能走一步或两步,请问有多少种走法。
设f(n)是n阶楼梯不同的走法。则转移到此状态的方法有两种,从n-1阶楼梯走一步上来,或者从n-2阶楼梯走两步上来。此时求解f(n)的任务只需扔给f(n-1)和f(n-2)即可,再定好初始值:f(1)=1,f(2)=2;
f(n)=f(1)+f(2)即可求解。
具体实现中应该维护一个 数组,减少重复运算。

Max Sum(HDOJ1003)

题目是给出一个自然数串,求最长子串,使之和最大。
这里建立状态数组时有些许不同,不能设f(n)是前n个数字构成数字串的Max Sum,这样虽然最后求解省事(就是f(n)),但是这样的话状态转移方程就不好写了。这种假设下,f(n)和前面f(i)关系比较复杂,不能简单地通过num[i]转换。
而如果将f(n)设为以n为结尾或者开始的子串的最大值,就要好一点。为什么呢?我们从最简单地方法来看,暴力搜索:

for (int i=1; i<=n; i++)
	for (int j=i; j<=n; j++){
		sum = 0;
		for (int k=i; k<=j; k++)
			sum += num[k];
		if (sum > max) max = sum;
	}
}

这里start和stop的值都是变化的,不像走楼梯问题那样起点是固定的,只有一个值在变化。这样设f(n)是以num[n]为结尾的子串的最大值,便固定了结尾,这样与f(n-1)的关系就是:f(n)=max(num[n], num[n]+f(n-1)) (f[1]=num[1])即如果前面的最大值都是负数,也没必要还要前面的子串了。
同理,设f(n)是以num[n]为结尾的子串的最大值,则f(n) = max(num[n],num[n]+f(n+1))
代码实现:

#include <iostream>
using namespace std;

int main(){
	ios::sync_with_stdio(false);
	int n, num[100001]={0}, f[100001]={0}, s[100001]={0};
	cin>>n;
	for (int ii=1; ii<=n; ii++){
		int nn;
		cin>>nn;
		for (int i=1; i<=nn; i++) cin>>num[i];
		f[1] = num[1];
		s[1] = 1;
		for (int i=2; i<=nn; i++){
			f[i] = num[i];
			if (f[i-1] < 0){
				s[i] = i;
			}else{
				f[i] += f[i-1];
				s[i] = s[i-1];
			}
		}
		
		int max = 1;
		for (int i=2; i<=nn; i++)
			if (f[i] > f[max])  max = i;
			
		cout<<"Case "<<ii<<":"<<endl;
		cout<<f[max]<<" "<<s[max]<<" "<<max<<endl;
		if (ii != n){
			cout<<endl;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值