动态规划阶段性总结(一)

2 篇文章 0 订阅
1 篇文章 0 订阅

动态规划阶段性总结(一)

动态规划与递推法(一)

从别的大佬那里学来可以写博客来记录自己的学习,那么我就尝试写一次

一、动态规划(递推法)的基本思想

这里说一下动规的基本思想,本思想仅仅截止于2020.6.26日,今后我如果对动规有新理解,将会持续更新。
首先动规讲究两个原则:
(我感觉目前我自己对这两个原则理解还不是很深刻,因为做的题还不够,就仅仅讲讲目前的理解,以作为记录)

1、最优化原则
一本通上有一句话说的很好:

一个问题的局部最优将导致整个问题最优

动态规划我目前遇到的题基本上都是用来解决最优解问题,例如找出最少花费啊,最优路径啊等等等等。而在解题的过程中,肯定是一步步求解,每一步就相当于一个子问题,在做题时你就会发现,你的每个子问题也是按着最优解去求的,所以最终的结果一定是最优的。

此处必须要提一下贪心法,有的小伙伴(包括我)之前也一直认为贪心法的每一步也是最优解,因为每次都会去找一个最好的嘛,但其实不然,贪心的局部看似最优,但最后往往得不到整体最优,所以局限性很大。具体与贪心的区别我以后大概会写吧

2.无后效性原则
一本通上又一句话说得好:

某阶段的状态一旦确定,则此后的过程演变不再受此前各状态及决策的影响

我的理解是什么呢,就是某一个决策一旦确定,那么你的这个决策一定是最优的,且对整体而言也是最优的,你以后的数据,决策绝对不会对之前的决策有任何的影响,但是你的之前的这个决策会对后面你即将做出的决策产生影响,这是一定的,毕竟一步步算嘛,前人铺路,后人乘凉嘛。

简单地说,决策一旦下达,便是一锤定音,不会再更改

如果你在写动规的过程中,遇到了前后决策纠缠不清的情况,大概要重新梳理一下思路了。

二、具体的例题上一上呗~

以下题目来自奥赛一本通,评测网站:http://ybt.ssoier.cn:8088/index.php

1、数字金字塔
【题目描述】
观察下面的数字金字塔。写一个程序查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以从当前点走到左下方的点也可以到达右下方的点。
在这里插入图片描述
在上面的样例中,从13到8到26到15到24的路径产生了最大的和86。
【题目描述】
第一个行包含R(1≤ R≤1000),表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

所有的被供应的整数是非负的且不大于100。
【输出】
单独的一行,包含那个可能得到的最大的和。
【输入样例】

5
13
11 8
12 7 26
6 14 15 8
12 7 13 24 11

【输出样例】

86

【思路】

哈哈

这个题可有意思了,作为入门引例真的再合适不过了

首先第一想法是不是贪心法亚

对嘛,我每一次都选个大的加上去,结果不就大了吗!
很遗憾,不对呀 阿sir~~
你康康,加入从第一个开始,每一次向下都选择数字最大的,那么就是

13 --> 11 --> 12 --> 14 -->13 == 63 < 86

不对呀,是吧。所以看来只能用动态规划了。
这道题告诉我们做人不要贪心亚,只顾眼前利益而不看长远,这定律感觉在各个领域都适用呢~~

那么问题来了,动态规划怎么搞呢?
这里我说一下正推法,至于倒退法请读者(我自己)自己去想!!!!(想不明白看书!!)
首先 我们要设立一个数组 b[i][j] ,存放一个很关键的东西:
当前决策的最优解的值
然后我们一步一步看
当我们从最顶部开始:13 此时没有别的点,就他一个,那么此时最优解就是他自己 b[1][1]=13
然后看第二层:11 和 8 此时两者都只连接了一个 13 所以此时这两个点的最优解 是他们自己的值加上之前确定好的最优值13

b[2][1]=13+11=24
b[2][2]=13+8=21

然后第三层
12这个点,只有一条路到达他 所以 b[3][1]=b[2][1]+12=45
7这个点,有两条路可以到达他 由于之前的决策已经确定,一定为最优解,所以选择一个最大的即可:b[3][2]=b[2][1]+7=31
(有同学可能会问了,这里你也是选最大的啊,为啥就不叫贪心法了呢?这里最大的区别就是上面的决策是已经确定好了的最优解,所以我敢直接加上去,这就是最微妙之处,请你后来看的时候务必细细体会这里,这里就是利用了无后效性原则,我敢加就是因为我知道后面的决策不会对前面有影响,但贪心就不一样了,后面的可能就会对前面造成影响,使其不是最优解)
26这个点,也只有一条路可以到达
b[3][3]=b[2][2]+26=47
按着这个思想,一步一步向下类推,最后的最大值就是答案咯

下面上代码啦!你一定能看明白的w!!!

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
int main(){
	int a[100][100];//数塔
	int b[100][100];//已算好的状态最优值
	int d[100][100];//用于记录路径,仅有0和1,0为向下走,1为向右走
	int n,maxx=0;
	scanf("%d",&n);
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(d,0,sizeof(d));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){
			cin>>a[i][j];
			b[i][j]=a[i][j];
		}
	}
	for(int i=2;i<=n;i++){
		for(int j=1;j<=i;j++){
			if(b[i-1][j-1]>b[i-1][j]){
				b[i][j]=a[i][j]+b[i-1][j-1];
				d[i-1][j-1]=1;
			}
			else {
				b[i][j]=a[i][j]+b[i-1][j];
				d[i-1][j]=0;
			}
		}
	}
	int temp;
	for(int i=1;i<=n;i++){
		if(b[n][i]>maxx){
			maxx=b[n][i];
			temp=i;
		}
	}
	cout<<maxx<<endl;
	/*cout<<a[1][1]<<"-->";
	int t=1;
	for(int i=2;i<=n-1;i++){
		t=t+d[i-1][t];
		cout<<a[i][t]<<"-->";
	}
	cout<<a[n][temp]<<endl; 
//return 0;*/
}


这里小小总结一下:
如果你觉得这个题能用动规做,那么一定就能找到一个最原始的一个部分,他的最优值是已经确定下来的,作为第一步决策,就像上面题中的第一个点13,然后再顺着一步一步的走下去。
另外,所给数据一定是有顺序的,或者是可以排序的,这样你才能写循环一步一步去求解。
(还有一个最长不下降子序列的题目,今天太晚了,明天写!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值