出入DP

 

做的第一道DP,是HDU上面的1003

题意是给你个数列,让你求出第一个指定序列的长度和它的起点和终点。(对于数列来说,第一个明显是1)

这个指定序列满足它是母序列的的所有子序列中和最大。

网上主要有三种解法:一:分治;二:DP(插头DP);三:贪心(disgussl里面有人说可用贪心做)

我最近在学习DP,就用DP来解决这一题。

出入DP这道大门,我先到油管上看了MIT的两个关于dynamic programming的公开课,里面的教授讲的还不错,可惜到了后面就听不太懂。

那个高高的男教授一开始是用斐波那契递归引入了DP这个个概念。

 

对于求解任意地点的斐波那契数,我们首先想的是递归算法;

 

#include <cstdio>
#include <iostream>
using namespace std;
long long fib(int n){
	if(n<3)
		return 1;
	else{
		return fib(n-1)+fib(n-2);
	}
}
int main(){
    int n;
    cin>>n;
    cout<<fib(n)<<endl;
    return 0;
}

没错,就是上面那个丑的要死版本。

但是,我相信一定有人这么干过。

后面,想到的是,这个程序如果要交到判题机上面的话,铁定爆栈,所以有人用下面这个方法(滚动数组)

#include <stdio.h>
using namespace std;
int main(){
    int n;
    long long fib[3];
    while(~scanf("%d",&n)){
        fib[1]=1,fib[2]=1;
        for(int i=3;i<=n;++i)
            fib[i%3]=fib[(i-1)%3]+fib[(i-2)%3];
        printf("%lld\n",fib[n%3]);
    }
    return 0;
}

用了滚动数组以后,就节省了空间和时间。

油管上的那位教授采用了全新的方法(HUA JI)DP来做了:

 

准确的来说应该是用记忆化来算:

#include <stdio.h>
#include <cstring>
using namespace std;
long long memo[1000000];
long long k;
long long fib(int n){
    if(n<3)
        return 1;
    if(memo[n]!=0)
        return memo[n];
    else{
        k=fib(n-1)+fib(n-2);
        memo[n]=k;
    }
    return k;
}
int main(){
    int n;
    while(~scanf("%d",&n)){
        memset(memo,0,sizeof(memo));
        long long ans=fib(n);
        printf("%lld\n",ans);
    }
    return 0;
}

这个就和他写的差不多。这段代码采用了memo数组保存了在递归过程中已经计算过了的值。这样做的好处就在于免去了大量的重复计算,从而节约了时间。

在《算法导论》的动态规划这一章中,作者用切钢条的例子,向我们展示了DP这个算法的产生和它的两种解法。

但在这之前,我们要明白什么样的问题适用于DP呢?

有两个原则:

一:最优性原理

不论初始状态和初始决策如何,对于前面决策所造成的某一状态而言,其后各阶段的决策序列必须构成最优策略”。(百度百科)

就是说,原问题可以分解为多个子问题,子问题的最优解一定是原问题的最优解。

二:无后效性

就是说原问题的最优解只取决于子问题的值(最优解),而与子问题的状态无关。

简单来说,无论子问题是那条路走过来的,原问题都毫不在乎,他只关心子问题的结果(无情)。

 

DP两种思路:
一:TOP->DOWN

即自顶向下,从原问题出发,利用递归和记忆化保存中间变量,向下求解子问题,最后回溯,合并,得到答案。

二:DOWN->TOP

自底向上,从子问题出发,一般会用到一个for 循环,牵涉到一个叫做“状态转移方程”的东东,这个东东不简单啊,你要去推导它。

假设我有一个函数叫 f(P),P是关于n维变量的一个集(x,y,z...),在这个集中,我把这些元素叫做一个状态,对应一个值(最优解,要计算)。

我还有一个决策函数Ф(P),P的定义同上,这个函数对应在P的状态下,我做的决策(选择的分支),那么一般的状态转移方程是{f(Pn)=max/min(f(Pn+1),f(Pn)),Ф(P)<-f(P)}

 

 

阅读更多

没有更多推荐了,返回首页