学习总结 线性dp的分析过程

动态规划
将问题分成一个一个的小部分,每个部分对应一个或几种状态,通过分析每个小部分的状态,找出一个子问题的各种状态与下一个子问题状态之间的关系,层层递进,最终找出问题的最终结果。
线性dp是动态规划问题较为简单的一种。
分析问题的一般步骤
1 阶段点的选取,或者说是基准的选取,这一步是所有dp问题的关键,只有找到合适的阶段,问题才有解决的可能。
2 找出阶段点所表示的含义,他具体代表什么,是代表他之前的阶段的最优值还是他之后的最优值,还是某种状态下的最优值,有时找到的是每种状态下的最优值,最后还要在dp【n】【i】(i属于1到n)中选择最优。
3 找出这个阶段点有几种状态,给每种状态找到一种表示,找出这种状态与之前的状态的关系,这时状态转移方程或者说转移关系就基本上明确了。
4然后将数据想办法处理,将关系用数组递推的形式表示出来,这或许很不好弄,可以缩小数据的范围,通过起始点以及向后的一些点来找找数据处理的过程和代码实现的过程中应该注意的点,也可以多借鉴,多学习,但阶段点的选取与阶段点状态的找出和状态与之前状态的关系的建立需要自己多学习总结思路和方法,找出自己所选取的阶段点与状态和优秀的思路和代码之间的差别,这很重要。代码或许仅靠自己是无法较好的完成的,但代码的大体过程以及每一步的目的一定要先明确下来,再去写,再去借鉴。
5 把握一个原则,原则上1秒钟执行1亿次循环循环体中的代码处理过程稍复杂的话就一定会超时,这时数据量是一个提示,会告诉你应该用几重循环来解决问题以及优化的大体方向是什么。
问题分析

给定 N (1 <= N <= 1000000) 个绝对值不超过 32768 的整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[N],求从该序列中取出 M 个连续不相交子段,使这 M 个子段的和最大。如果子段全都是负数则最大子段和也为负数。
输入:有一个正整数 M 和 一个正整数 N,后面紧跟 N 个绝对值不大于 32768 的整数
输出:最大 M 子段和
样例输入:
2 6 -1 4 -2 3 -2 3
样例输出:
8
分析过程
阶段点 以每个数为一个阶段点,每个阶段点代表他之前种种的情况的最优值,但是,还有段数的问题,那么,显然用一个一维数组是不行的,应该是二维,另一个是表示段数的情况,那么这个阶段点就是f【i】【j】,表示前i个数分成j段的最优值。
阶段点的状态 每个阶段点应该有两种状态,一种是最后一个数与之前的数为一组的情况,一种是最后一个数自己与为一组的情况,那么f【i】【j】=f【i-1】【j】+a【i】,(单独情况),f【i】【j】=f【k】【j-1】+a【i】(自己为一组的情况),k要保证j组够分。
那么 第一种状态也要分两种情况,既然最后一个不单独成组,那么i-1要够分j组,不够分就设为极小值(有负数的情况若为0可能造成不好的影响)够分再加
a【i】,第二种状态要取最大值,就是从j-1个数到i-1个数分成j-1个组的情况中挑出最大值。
到这里,每一步都基本上明确了。

#include <iostream>
#include<cstdio>
using namespace std;
int a[1005];
int f[1005][1005];
int main()
{
    int m,n;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    int ans=-0x3f3f3f3f;
    for(int j=1;j<=m;j++)
    {
        for(int i=j;i<=n;i++)
        {
            if(i-1<j)
            {
                f[i][j]=-0x3f3f3f3f;
            }
        else f[i][j]=f[i-1][j];
        for(int k=j-1;k<i;k++)
        {
            f[i][j]=max(f[i][j],f[k][j-1]);
        }
        f[i][j]=f[i][j]+a[i];
        }
    }
    printf("%d\n",f[n][m]);
    return 0;
}

但这题的数据量过大,1000010000m很可能超过一亿,因而要想办法优化,减少循环次数,减少数组大小。
那么就去找代码里重复循环的东西,重复使用的东西,把他换掉。有了这样的思路之后,再去借鉴方法时目的就很明确了。(1滚动数组,2记住有用的状态值)。

小zc现在有三个字符串,他想知道前两个字符串能不能生成第三个字符串,生成规则如下:第一个串的每个字符都可以往第二个串的任意位置插入(包括首尾位置),但必须保证来源于第一个串中的字符在生成后的串中的相对顺序不可以改变。
举个例子:
String A: cu
String B: mt
那么,A和B可以生成的所有字符串是{cumt,cmut,cmtu,mcut,mctu,mtcu},而不能生成ucmt,因为uc来源于A串,但改变了A中字符原来的相对顺序。
但小zc觉得这个问题太简单,于是他想让你计算对于给定的A, B, C串,共有多少种方案能够生成C串。方便起见,你只需要输出最后答案对1000000007取模的值。

分析过程,阶段点,以c串为基准选取阶段点,既然是匹配c串,那么,c串上的每一个点都可以作为一个阶段点,表示c串上的这个点被匹配成功后的方案数,既然是用a,b两个串来匹配,那么就应该是二维数组来表示,然后,就是阶段点的状态,那么状态要涵盖面广,就是要包含匹配到c串某一个字符后,所有的方案数,那么f【i】【j】就可以表示a串用了i个,b串用了j个之后所包含的方案数,然后再对f【i】【j】进行分析,分三种情况,一种是只有a的情况,一种是只有b的情况,然后是既有a又有b的情况,
f【0】【j】=f【0】【j-1】,只有a的情况,只有b的情况与之类似。
f【0】【0】=1,如果a为零个b为零个c为零个,那么方案是一。
两者都有的情况那么匹配成功,则s【i】=s【i+j】,表示a的,j表示b的,
f【i】【j】=f【i-1】【j】+f【i】【j】;分析到这里,再去写代码基本上就能写个大概的样子出来了,但是,仍有很大的可能不完善,或者有缺失,这些需要多看一些优秀的代码,多学习其中的技巧。

农夫约翰的牛想跳上月亮,就像牛在他们最喜欢的童谣。不幸的是,牛不能跳。当地的巫医把P(1<=P<=150,000)药水混合在一起,以帮助牛寻求跳跃。这些药剂必须按照它们创建的顺序正确地进行管理,尽管有些药剂可能会被跳过。每种药剂都有一个“力量”(1<=强度<500),可以增强牛的跳跃能力。在奇数的时间步骤中服用药剂会增加牛的跳跃;在偶数的时间步骤中服用药剂会减少跳跃。在服用任何药剂之前,牛的跳跃能力当然是0。任何药剂都不能服用两次,一旦牛开始服用药剂,必须在每一时间步骤中服用一种药剂,从时间1开始。每轮可以跳过一个或多个药剂。确定要服用哪种药剂才能跳得最高。

Line 1: A single integer, P

  • Lines 2…P+1: Each line contains a single integer that is the strength of a potion. Line 2 gives the strength of the first potion; line 3 gives the strength of the second potion; and so on.
    Output
  • Line 1: A single integer that is the maximum possible jump.
    Sample Input
    8
    7
    2
    1
    8
    4
    3
    5
    6
    题目分析
    阶段点 以每个时刻为阶段点,由于该题的数据量为150000,所以显然要用一重循环,每个阶段点表示选择该时刻服用药物后所能达到的最大高度,但又分奇数时刻和偶数时刻,因而每个点对应两个状态,一个表示奇数时刻,一个表示偶数时刻,f[i][1]=max(f[i-1][1],f[i-1][0]+a[i]); f[i][0]=max(f[i-1][0],f[i-1][1]-a[i]);
    注意一点,偶数个与偶数个状态比较,奇数个与奇数个比较,不然就乱套了。

将N分为若干个不同整数的和,有多少种不同的划分方式,例如:n = 6,{6} {1,5} {2,4} {1,2,3},共4种。由于数据较大,输出Mod 10^9 + 7的结果即可。
Input
输入1个数N(1 <= N <= 50000)。
Output
输出划分的数量Mod 10^9 + 7。
Sample Input
6
Sample Output
4
这题阶段点的选取很重要,将1~n中每一个数字作为一个阶段点,,每个阶段点对应的状态有很多,一个阶段点对应这些状态----这个点分为1个,两个,。。。。多个数的和的方案数,由于状态很多,所以,用f[i][j]表示数i分成j个数的方案数,那么最终的方案数就等于f【n】【1.。。。m】相加。那么接下来就是找出状态之间的关系,那么f【i】【j】如何由之前得到呢?这是个问题。
这时可以列表,一到n为行,一到m为列,写出一部分数来,再找找规律,类似放苹果那道题。

dp问题很难,但是一点一点的按部分分析,明确每一步的目的,即使可能不能独立的完成某个问题,但是每一步的目的明确,分析过程明确,离最终的目标总会一点一点慢慢接近,如果上来就想写出状态转移方程,这是不可能的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值