初学算法----动态规划

动态规划的特点:

1.与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。

 

 

----------------------------------------------------------------------------------------------------------------------

写动态规划的一般步骤:

1.确定dp数组的定义 以及 dp数组下标的含义:
 
 (1)
dp数组的定义要满足最优子结构;

   (2dp数组的定义要满足无后效性;

2.写出递推公式;

3.根据dp数组的定义与公式判断边界条件(即最开始能通过什么递推出全部结果)

  (也可以说成确定dp数组的初始化)

4.列表,确定遍历顺序

----------------------------------------------------------------------------------------------------------------

 

 

 

 

大佬的好文章:

可能有人会问,动态规划与递归有什么区别 ?有一个很重要的点是,递归是自顶向下,拿最经典的斐波那契数列来举例(虽然这并不是典型的dp问题),如果是递归,那么公式会是f(n) = f(n - 1) + f(n - 2),而动态规划是 自底向上,即这时候的推导会是:f(3) = f(2) + f(1), f(4) = f(3) + f(2) ……看起来区别不大,有一个很关键的点:递归会有多余的重复求解动作,而动态规划不会。那么如果是剪枝后的递归呢?

 

​ 这时候就要明确动态规划的作用机理。递归的作用机理是自顶向下地把问题变小,并且一直调用自身。而动态规划的实质是:有限状态机。实际上动态规划就是把所有的情况都列出来,每一种情况就是一种 状态,状态与状态之间会发生转换,转换过程发生的变化就是 状态转移方程。而为什么动态规划一定要自底向上?因为我们的求解过程就是从状态机的 初始状态(一般叫base case),转换到 最终状态(也就是最后的结果)。如果查看其他的文章,可能会看到很多奇怪的概念,具体但不仅限于”无后效性“,”最优子结构“。这些概念实质上都不关键,我们只需要知道,动态规划实质上就是使用有限状态机的思想去求解,每一个状态实际上就是一个解(子问题的解),在所有的 可行解空间 内,寻找最优解。

 

​ DP就是为了列出所有的解空间(每一个状态就是一个子问题的解),进而求出最优解(最终状态的解)。那么看到”所有“这个字眼,看起来时间复杂度会很高,实际上并不是。因为DP自带剪枝,这主要是状态转移方程的功劳,它的转移实质上就是从一个子问题的最优解,得到一个更大问题的最优解,所以它会舍弃一大堆不可能成为最优解的答案。

 

​ DP的时间复杂度就是 O(解空间的大小) ,只要合理地设计,那么很多时候时间复杂度会是O(N * k * j),k,j是题目中的常数,所以它的效率很快(只要k,j等等的常数不会太大,那么就是近似O(N))。即列出所有解空间,顺序遍历,从初始状态一直转换到最终状态,最终状态就是结果。

 


————————————————
版权声明:本文为CSDN博主「ifrank98」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a445673440/article/details/110201591

 

一个问题是该用递推、贪心、搜索还是动态规划,完全是由这个问题本身阶段间状态的转移方式决定的!
每个阶段只有一个状态->递推;
每个阶段的最优状态都是由上一个阶段的最优状态得到的->贪心;
每个阶段的最优状态是由之前所有阶段的状态的组合得到的->搜索;
每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->动态规划。(无后效性)


————————————————
版权声明:本文为CSDN博主「minGW_Lee」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/github_34606293/article/details/78482523

 //-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

help jimmy的题目:这里很特殊,他分成了两个子问题---从左边下去的最短时间与从右边下去的最短时间

于是就有了这样的结构:在第k块板子上到地面的最短时间==  min(  从左边下去,从右边下去);//这个判断只要在最后用一下;

从左边下去,从右边下去正是dp数组要记录的;

从左边下去和从右边下去,下落的板子可能完全不同;

从左边下去的最短时间== min(  跳到下块板子上然后从左边下去的时间(dp记录的) +从第k块板子的左边缘到下块板子的左边缘的时间,  跳到下块板子上然后从右边下去的时间(dp记录的) +从第k块板子的左边缘到下块板子的右边缘的时间)+下落的时间;//从左边下去后,肯定是面临左右跑到问题,min判断;

从右边下去的最短时间同理;

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MAX_NUM 2000
#define MAX_TIME 200000
struct str
{
    int sl, sr, h; //左横坐标,右横坐标,高度;
} a[MAX_NUM];
int t, n, x, y, MAX, dp[MAX_NUM][2]; // dp数组,把从左边下去的时间保存到dp[i][0]中,把从右边下去的时间保存到dp[i][0]中,dp数组的含义是从第i块板子分别从左和右下去的最小时间;
bool rule(str a, str b)
{
    return a.h < b.h;
}
void lefttime(int k)
{
    int num = k;
    while (--num) //判断是否下面有板子;
    {
        if (a[k].sl >= a[num].sl && a[k].sl <= a[num].sr) //看一下是否能跳到下一块板子;
        {
            if (a[k].h - a[num].h > MAX) //能跳到下一块板子,但是直接跳下去有问题;
            {
                dp[k][0] = MAX_TIME;
                return;
            }
            else
            {
                dp[k][0] = min(dp[num][0] + a[k].sl - a[num].sl, dp[num][1] + a[num].sr - a[k].sl) + a[k].h - a[num].h;
                return;
            }
        }
        //不能跳到下一块板子,继续寻找板子;
    }
    //出来了说明没有板子了,是地面,那就有两种情况;
    if (a[k].h <= MAX)
    {
        dp[k][0] = a[k].h; //直接跳下去没有问题;
    }
    else //下不去了;
    {
        dp[k][0] = MAX_TIME;
    }
}
void righttime(int k)
{
    //基本与上面一样,只要走的方向改一下;
    int num = k;
    while (--num)
    {
        if (a[k].sr >= a[num].sl && a[k].sr <= a[num].sr)
        {
            if (a[k].h - a[num].h > MAX)
            {
                dp[k][1] = MAX_TIME;
                return;
            }
            else
            {
                dp[k][1] = min(dp[num][0] + a[k].sr - a[num].sl, dp[num][1] + a[num].sr - a[k].sr) + a[k].h - a[num].h;
                return;
            }
        }
    }
    if (a[k].h <= MAX)
    {
        dp[k][1] = a[k].h;
    }
    else
    {
        dp[k][1] = MAX_TIME;
    }
}
int main()
{
    cin >> t;
    while (t--)
    {
        cin >> n >> x >> y >> MAX;
        a[1].sl = a[1].sr = x;
        a[1].h = y;
        for (int i = 2; i <= n + 1; i++)
        {
            cin >> a[i].sl >> a[i].sr >> a[i].h;
        }
        sort(a + 1, a + n + 2, rule);    //排序,使板子更高的序号更大;
        for (int i = 1; i <= n + 1; i++) //递推,从下向上把最优时间全部推出;
        {
            lefttime(i); //这样分别求才能相互调用到想要的mintime;
            righttime(i);
        }
        printf("%d\n", min(dp[n + 1][0], dp[n + 1][1]));
    }
    return 0;
}
posted @ 2022-01-04 23:22  次林梦叶  阅读(59)  评论(0)    收藏  举报