动态规划

一.特性:

    (1)最优子结构性质。即问题的最优解所包含的子问题的解也是最优的。

    (2)子问题重叠性质。在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,

有些子问题会被重复计算多次,利用子问题的重叠性质,对于每个子问题只计算一次,然后将结果保存

起来,下次需要重新计算已经计算过的的问题查询结果即可。

    (3)无后效性:每个状态都是过去历史的一个完整总结。

二.Dp分类

    简单的Dp:

        (1)递推:一般形式单一,从前往后分类枚举即可。

        (2)背包:0-1背包、完全背包、分组背包、多重背包

        (3)LIS:最长递增子序列,朴素的LIS是复杂度为O(n2)的算法,二分下的LIS是复杂度O(nlog2n)的算法。

        (4)LCS:最长公共子序列,通常时间复杂度为O(n2)的算法

    区间Dp:

        枚举将区间分成左右两部分,然后求出左右区间再合并。

    树形Dp:

        基于在树上的数据结构,通过DFS维护从根到叶子或从叶子到根的状态转移

三.解题三部曲

    第一步:确定状态

    第二部:确定状态转移方程

    第三步:确定编程实现方式

 

    问题1:令 I 是一个 n 位十进制整数,如果将 I 划分为 k 段,可得到 k 个整数。这 k 个整数的乘积称为 I 的一个 k 

乘积。对于给定的 n 、k 和 I,求出 I 的最大 k 乘积。

    (一):确定状态:dp[ i ][ j ]: 表示前 i 个数,分成 j 段

    (二):确定状态转移方程: dp[ i ][ j ] = max(dp[ i ][ j ], dp[ k ][ j - 1] * val[ k + 1][ i ]);

    (三):确定编程实现方式

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 20;
 4 char ch;
 5 int dp[maxn][maxn];
 6 int num[maxn],val[maxn][maxn];
 7 int main()
 8 {
 9     int n,k;       // n 位 k 段
10     while(~scanf("%d%d",&n,&k))
11     {
12         memset(val,0,sizeof(val));
13         for(int i=1;i<=n;i++)
14         {
15             cin>>ch;
16             num[i] = ch - '0';
17         }
18 
19         for(int i=1;i<=n;i++) val[i][i]=num[i];
20 
21         for(int i=1;i<=n;i++)
22             for(int j=i+1;j<=n;j++)
23                 val[i][j] = val[i][j-1]*10 + val[j][j];  //表示的是存放 i 到 j 所表示的整数。
24 
25         for(int i=1;i<=n;i++) dp[i][1] = val[1][i];     //只分成一段从第一位到i 位数字。
26 
27         for(int i=1;i<=n;i++)//位数
28             for(int j=0;j<i;j++)//分割段数
29                 for(int k=1;k<i;k++)
30                     dp[i][j]=max(dp[i][j],dp[k][j-1]*val[k+1][i]);
31 
32         cout<<dp[n][k]<<endl;
33     }
34     return 0;
35 }

    问题二: n 个整数(可能含有负数)组成的序列 a1,a2, ... , an,求该序列字段和的最大值,当所有整数都为负数时

定义其最大子段和为0。

   

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 ll a[100000];
 5 int main()
 6 {
 7     int n;
 8     ll sum = 0, temp = 0;
 9     cin>>n;
10     for(int i = 1; i <= n; i++)
11     {
12         cin>>a[i];
13         if(temp > 0) temp += a[i];
14         else temp = a[i];
15         sum = max(temp,sum);
16     }
17     cout<<sum<<endl;
18     return 0;
19 }

    问题三: 找出由 n 个 数字组成的序列的最长单调递增子序列。

    核心代码:

ll CalcMaxL(ll b[],int n)  //计算序列最长递增子序列的长度
{
    ll t = 0;
    for(int i = 0; i < n; i++)
        if(b[i] > t)
            t = b[i];
    return t;
}
ll DpLongestIncreasingSequence(ll a[], ll b[], int n)  //数组 b[]记录 0 ~ i 为结尾的最长递增子序列 
{
    int i, j, k;
    for(i = 1, b[0] = 1; i < n; i++)
    {
        for(j = 0, k = 0; j < i; j++)
            if((a[j] <= a[i]) && (k < b[j]))
                k = b[j];
        b[i] = k - 1;
    }
    return CalcMaxL(b,n);
}

 

posted @ 2019-11-02 16:58  留幸愉  阅读(165)  评论(0编辑  收藏  举报