动态规划

  百度百科说:动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程的优化问题时,提出了著名的最优化原理,把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。ios

适用条件算法

任何思想方法都有必定的局限性,超出了特定条件,它就失去了做用。一样,动态规划也并非万能的。适用动态规划的问题必须知足最优化原理和无后效性。ide

1.最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具备这样的性质,不论过去状态和决策如何,对前面的决策所造成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略老是最优的。一个问题知足最优化原理又称其具备最优子结构性质。优化

2.无后效性将各阶段按照必定的次序排列好以后,对于某个给定的阶段状态,它之前各阶段的状态没法直接影响它将来的决策,而只能经过当前的这个状态。换句话说,每一个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。spa

3.子问题的重叠性 动态规划将原来具备指数级时间复杂度的搜索算法改进成了具备多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程当中,不得不存储产生过程当中的各类状态,因此它的空间复杂度要大于其它的算法。设计

状态转移方程,是动态规划中本阶段的状态每每是上一阶段状态和上一阶段决策的结果。若是给定了第K阶段的状态Sk以及决策uk(Sk),则第K+1阶段的状态Sk+1也就彻底肯定。code

一般状况下,在肯定一个题须要用到动规时,咱们要先肯定出他的状态转移方程。根据状态转移方程写出代码。blog

必定要注意边界条件。状态转移方程要从第二层开始用。ci

来看几个栗子;get

例1:codevs 3409 搬运礼物传送门

小浣熊松松特别喜欢交朋友,今年松松生日,就有N个朋友给他送礼物。但是要把这些礼物搬回家是一件很困难的事,具体来讲,若是松松一次搬运x件礼物,就要花费w[x]的体力(显而易见,有w[x]<=w[x+1],搬得越多耗费体力越多)。松松并不在乎他会搬多少次,可是他想知道,本身最少花费多少体力,就能够把礼物所有搬回家。

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
    int a[15111],n;
    cin>>n;
    for(int i=1;i<=n;++i)
        cin>>a[i];
    for(int i=1;i<=n;++i)
        for(int j=1;j<i;++j)
            a[i]=min(a[i-j]+a[j],a[i]);
    cout<<a[n];
    return 0;
}
Code

 

例二:codevs 1044 拦截导弹传送门

a[i]记录运送i件礼物最小体力花费,初值为输入值,经过递推计算出N件礼物最小体力花费。

  某国为了防护敌国的导弹袭击,发展出一种导弹拦截系统。可是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹可以到达任意的高度,可是之后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。因为该系统还在试用阶段,因此只有一套系统,所以有可能不能拦截全部的导弹。

#include<iostream>
#include<algorithm>
using namespace std;
int n=1;
int a[1005];
int dp[1005];

int xulie1() 
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(a[j]>a[i])
                dp[i]=max(dp[j]+1,dp[i]);
    int maxl=-1;
    for(int i=1;i<=n;++i)
        maxl=max(maxl,dp[i]);
    return maxl;
}

int xulie2()
{
    for(int i=1;i<=n;i++)
        dp[i]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(a[j]<=a[i])
                dp[i]=max(dp[j]+1,dp[i]);
    int maxl=-1;
    for(int i=1;i<=n;++i)
        maxl=max(maxl,dp[i]);
    return maxl;
}

int main() 
{
    while(cin>>a[n]) n++;
    cout<<xulie1()<<endl;
    cout<<xulie2();
}
Code

 

例3:codevs 5294 挖地雷传送门

在一个地图上有N个地窖(N<=20),每一个地窖中埋有必定数量的地雷。同时,给出地窖之间的链接路径。当地窖及其链接的数据给出以后,某人能够从第一个地窖开始挖地雷,而后能够沿着指出的链接往下挖(仅能选择一条路径),当无链接时挖地雷工做结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。

思路:因为能够从任何一个地窖开始挖,因此要从每一个点开始递推;

#include<iostream>
using namespace std;

int s[15111],num[15111];
int n;
bool sum[15111][15111];
int path[15111];

int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)
        cin>>s[n];
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
            cin>>sum[i][j];
    for(int i=n;i>=1;--i)
        for(int j=i;j<=n;++j)
            if(sum[i][j])
            {
                if(num[i]<num[j]+s[i])
                {
                    num[i]=num[j]+s[i];
                    path[i]=j;
                }
            }
            else num[i]=max(num[i],s[i]);
    int maxl=0,k=0;
    for(int i=1;i<=n;++i)
        if(num[i]>maxl)
        {
            maxl=num[i];
            k=i;
        }
    int qu=k;
    while(k!=0)
    {
        cout<<k<<' ';
        k=path[k];
    }
    cout<<endl;
    cout<<maxl;
}
Code

 

例5:codevs 1048 石子归并传送门

有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并能够合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,可以使得总合并代价达到最小。

思路:递推和记忆化搜索

#include<iostream>
#include<cstring>
using namespace std;

int dp[1005][1005];
int s[1005];
int n;

int main()
{
    s[1]=0;
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        int sum;
        cin>>sum;
        s[i]=s[i-1]+sum;
    }
    memset(dp,0x3f,sizeof(dp));
    for(int i=1;i<=n;++i)dp[i][i]=0;
    for(int i=n;i>0;--i)
        for(int j=i;j<=n;++j)
            for(int k=i;k<j;++k)
            dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]);
    cout<<dp[1][n];
    return 0;
}
递推
相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息