您的位置:首页 > 理论基础 > 数据结构算法

动态规划 总结

2017-08-22 15:48 211 查看
   http://www.howzhi.com/note/3025

1.首先搞清楚一些名词。

     最优子结构:比如有时候有些问题求最短路径,最大的容量类似这样的方式,我们采取的最优解的方法组合。

    
重叠子问题:  类似于fib的问题,重复出现。

可以参考 科大oj1851 聚宝盆问题

题意就是钱生钱,每个三天生钱一次。

在求解一个问题时,相同的子问题可能会出现许多次。深度优先搜索之类的方法,对 于某个子问题,不管是否已经求解过,只要遇上,就会再次对它求解,因而影响了解题的 效率。而动态规划对每个子问题只计算一次并存储起来,下次需要时取用即可。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cstdlib>

using namespace std;
int fib[100];
int jvbaopen(int n)
{
if(n==1||n==2)
return 1;
if(fib
!=0)
return fib
;
return fib
=jvbaopen(n-1)+jvbaopen(n-2);

}

int main()
{
int n;
memset(fib,0,sizeof(fib));
while(cin>>n)
{
if(n==0) break;
jvbaopen(n);
cout<<jvbaopen(n)<<endl;
}

return 0;
}

现在以及认识了DP里面的专有名词,现在可以考虑要怎么实现DP了。

换句话说,怎么用,有什么方式。

2.方式:自顶向下,自底向上。

  (1)  先讨论自顶向下。

就类似于递归,现在又有一个专业名词叫做记忆化搜索,可参见刘汝佳的紫书P260页。有详细的概念。

搞清楚 状态转移方程。

可以这样思考设置当前的位置为一个点(i,j),这个点可以动的,可以左边移动,可以右边移动,那么从当前位置移动到另外一个位置所产生的距离要存起来,设置一个指标函数d(i,j),定义状态(i,j)的指标函数d(i,j)为格子(i,j)出发时能到得到的最大和,同时也包括(i,j)本身。

向左走,状态为(i+1,j),从当前(i+1,j)这个状态出发后能得到最大和,即d(i+1,j).

同样向右走,d(i+1,j+1);但是可以在这两个决策中自由选择哪个是最大的。

得到了状态转移方程:

d(i,j)=a(i,j)+max{d(i+1,j),d(i+1,j+1)};

举个例子 数塔问题(可以去杭电搜,这些天多校赛关了,搜不到。。。)

#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[110][110];
int data[110][110];
int shuta(int i,int j,int n){
if(data[i][j]>=0) return data[i][j];
return data[i][j]=a[i][j]+(i==n?0:max(shuta(i+1,j,n),shuta(i+1,j+1,n)));
}
int main()
{
int C;

while(cin>>C){
int N;
cin>>N;
memset(data,-1,sizeof(data));
for(int i=1;i<=N;i++)
for(int j=1;j<=i;j++)
cin>>a[i][j];
cout<<shuta(1,1,N)<<endl;
}
return 0;
}

可以结合紫书再看。

我在这里面学会了memset的用法,原来只晓得批量赋值的参数只为0,原来可以置为-1(0xff)。

今天还问了边上的一位大佬,要根据情况来定,要改点数字,置0置-1均可。

记忆化搜索中将d全部初始化为-1,使用memset(d,-1,sizeof(d));

根据数塔这题可以分析

if(d[i][j]>=0) return d[i][j];

为什么要这么做?

程序是递归的,当d为负值,即可判断是否大于等于0,得知它是否被计算过。

return语句表示返回,可以将d存在里面。

今天学了这些。

     
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息