ACM0基础动态规划启蒙:Dp是个啥

动态规划:DP:Dynamic Programming是算法的设计方法,是一种编程思想,主要用于解决最优解、计数类型的问题。

Dp是一种思想!!!是一种没有固定代码的算法。

如果不是一定要有严谨的记忆化,状态意义,状态转移方程,步数合并的过程才叫Dp,那么不妨把一些奇奇怪怪的东西也和Dp扯上关系

0基础新人的第一个启蒙算法应该大部分都是 Dfs ,好我现在就把这个东西叫做Dp。

Question:Dfs为什么叫Dp?

Dp需要步数合并对吧,把一些没必要的操作给省略掉或者一些可以合并的操作給合并。

举个例子:跳台阶
0为起点,n为终点每次只能跳一步或者跳两步,也就是产生 两种选择。求从0到恰好跳到n的过程中有多少种跳跃方式?
eg : n等于4的时候答案为5
序列分别是
1111
121
112
211
22

对于这个题目,新手很容易能想到Dfs,也就是从1开始搜索每一个点的两次抉择,也就是2^n次抉择的超暴力dfs。
程序也非常容易写出来:

int dfs(int k)//函数功能:返回0到k的步数可能序列;
{
	if(k<=2)return k;
	else return dfs(k-1)+dfs(k-2);//当然是等于前两个啦。
}

但是你看一下,有些东西不对劲哦:
你看看程序访问了什么东西

5
4
3
3
2
1
2
1
1
1
2
1

wow,你的搜索有够 暴力 的,你注意到3被重复搜索了,2也被重复了,天哪比3还多,oh,1竟然被访问了5次。
你手算了一下n等于更大的时候注意到自己竟然有如此多没有必要存在的操作。
于是你开了个数组并且命名为remember[];
紧急修改你的暴力dfs代码;

int rem[10000+10];
int dfs(int k)//函数功能:返回0到k的步数可能序列;
{
	if(rem[k])return rem[k];//如果已经被访问过直接返回结果
	if(k<=2)return k;
	else return rem[k] = dfs(k-2)+dfs(k-1);//要记得赋值更新哦。
}

好耶,现在你在算算时间花了多少。好,O(n)。
你相当于用大剪刀把树枝给减掉了。用的方法是记忆下来结果,避免重复操作。

恭喜你,你的Dp入门了。

接下来解释几个名词:
状态储存:像刚才一样,每一个“局面”也就是树上的点是我们访问过的状态,用rem[i]记录。
状态意义:状态的准确意义,比如rem[i] = 0-k步的操作可能序列
状态转移:rem[i] = ?谁对这个状态有贡献,或者说取决于谁。
状态转移方程:rem[i] = rem[i-1]+rem[i-2];

上面代码就是记忆化Dfs,顾名思义就是dfs同时记忆结果减少重复访问。Dp某种意义是和记忆化dfs相等的。

这个时候不要兴冲冲就去写了,你漏一个至关重要的东西,这个东西非常隐秘而且对于新手或者粗心的代码者来说相当易错:边界

如果你写成

int dp[10000+10];
int Dp(int k)//函数功能:返回0到k的步数可能序列;
{
	for(int i =1;i<=k;++i){dp[i]=dp[i-1]+dp[i-2];}
}

你甚至越界了,注意边界还有保证一些东西的初始化,比如:

int dp[10000+10];
int Dp(int k)//函数功能:返回0到k的步数可能序列;
{
	dp[1]=1;dp[2]=2;
	for(int i =3;i<=k;++i){dp[i]=dp[i-1]+dp[i-2];}
}

当然如果真的n这么大会爆long long,一般题目还会存在模数,这里不做讨论。

现在你已经懵懵懂懂对Dp有大概了解了,是时候来点真题了:
过河卒
方格取数

最大子段和
洛谷可以看题解,题解自己研究吧,都是适合入门的普及-难度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值