动态规划 总结
2017-08-22 15:48
211 查看
http://www.howzhi.com/note/3025
1.首先搞清楚一些名词。
最优子结构:比如有时候有些问题求最短路径,最大的容量类似这样的方式,我们采取的最优解的方法组合。
重叠子问题: 类似于fib的问题,重复出现。
可以参考 科大oj1851 聚宝盆问题
题意就是钱生钱,每个三天生钱一次。
在求解一个问题时,相同的子问题可能会出现许多次。深度优先搜索之类的方法,对 于某个子问题,不管是否已经求解过,只要遇上,就会再次对它求解,因而影响了解题的 效率。而动态规划对每个子问题只计算一次并存储起来,下次需要时取用即可。
现在以及认识了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存在里面。
今天学了这些。
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存在里面。
今天学了这些。