【数据结构算法】动态规划之【单序列问题】

目录

1.序言:了解动态规划

2.最小花费爬楼梯:

分析问题:

确定状态转移方程:

 代码实现:

3.最大子数组和

 分析问题:

状态转移方程:

代码实现:

4.打家劫舍

状态转移方程: 

代码实现:

改进:

5. 粉刷房子

问题分析:

状态转移方程:

代码实现:


1.序言:了解动态规划

浅浅的谈一谈我对动态规划的认识:

动态规划常常适用于有重叠子问题和最优子结构性质的问题,并且记录所有子问题的结果,因此动态规划方法所耗时间往往远少于朴素解法。 动态规划有自底向上和自顶向下两种解决问题的方式。自顶向下即记忆化递归,自底向上就是递推。 使用动态规划解决的问题有个明显的特点,一旦一个子问题的求解得到结果,以后的计算过程就不会修改它,这样的特点叫做无后效性,求解问题的过程形成了一张有向无环图。动态规划只解决每个子问题一次,具有天然剪枝的功能,从而减少计算量。

找出描述的大问题的解和小问题之间的解递归关系或者迭代关系的状态转移方程是利用动态规划解决问题的关键;

接下来 我们将会用一个个列题带大家来熟悉一下动态规划中的单序列问题;

2.最小花费爬楼梯:

分析问题:

爬楼梯 一次只能怕=爬一层或者两层,每一步都两个选择;也许看起来很像可以用回溯法来解,但是这个问题不是来求有多少种方法来去爬到楼梯顶;这是一个求有最小的花费问题(最优解问题),所以咱们这里用动态规划来解题;

确定状态转移方程:

既然是求最小成本,那么每一步的走法就可以分解成两个子问题,每一层可以选择爬一阶或者两阶楼梯,设 f [ i ] 为第 i 阶向上爬的最小成本,f [ i ]  如何取最小?这和 f [ i - 1 ] , f [ i - 2 ] 的值有关,就是取这两个值的最小值;即状态转移方程为:f [ i ] = min( f [ i - 1] +  cost [ i  - 1] , f [ i - 2 ] +  cost [ i  - 2] )

 代码实现:

int min(int a,int b)
{
    return a>b?b:a;
}
int minCostClimbingStairs(int* cost, int costSize){
    int i = 0;
    int f[10000] = {0,0};
    for(i = 2;i<=costSize;i++)
    {
        f[i] = min(f[i-1]+cost[i-1],f[i-2]+cost[i-2]);
    }
    return f[costSize];
}

3.最大子数组和

 分析问题:

这一题和上一节当中讲到的【寂寞如雪】这题很像,基本是就是相同的问题;我们要求最大的子数组和,同样的我们要一步一步的分解,以第 i 个整数结尾的子树组分为两种情况 1.和第 i - 1 个整数结尾的子数组相连;2.单独以第 i 个整数作为子数组;

状态转移方程:

每一步都两个选择 以前一个是 i - 1的子数组 或者 以 单个  i  结尾的子数组,选好当下的每一步才能解出此题;设 f [ i ] 为最大子数组和,状态转移方程为 : f [ i ] = max( f [ i - 1]  + nums[ i ] ,  nums[ i ] ) ; 举个简单的例子 nums = { -2 , 1} ; f [ 0 ] = { - 2 } ;f [ 1 ] = max( f [ 0 ] + nums[ 1 ] , nums[ 1 ] ) = { 1 };

代码实现:

int maxSubArray(int* nums, int numsSize) {
    int f[2] = { 0 };
    int i = 0;
    f[0] = nums[0];
    int maxvalue = nums[0];
    for (i = 1; i < numsSize; i++)
    {
        f[i % 2] = max(f[(i - 1) % 2] + nums[i], nums[i]);
        maxvalue = max(max(f[0], f[1]), maxvalue);
    }
    return maxvalue;
}

4.打家劫舍

题目都是类似;分析的情况一笔带过:

状态转移方程: 

题目的意思是不能偷相邻俩家的房子,不然会被发现,求偷盗的最大金额;设 f [ i ] 为小偷从 0 开始偷 到 i 的最大金额数;在标号为 i 的房子面前,小偷可以选择偷或者不偷,偷的话 i - 1不能偷,此时得到的金额为 f [ i - 2 ] + nums [ i ];如果不偷,此时的金额为 f [ i - 1 ];由此可以得出状态转移方程:  f [ i ] = max ( f [ i - 1 ] , f [ i - 2 ] + nums [ i ] );

代码实现:

int max(int x,int y){
    return x>y?x:y;
}

int rob(int* nums, int numsSize){
    int i = 0;
    int f[1000];
    if(numsSize == 2)
        return max(nums[0],nums[1]);
    if(numsSize == 1)
        return nums[0];
    f[0] = nums[0];
    f[1] = max(nums[0],nums[1]);
    for(i = 2;i<numsSize;i++)
    {
        f[i] = max(f[i-1],f[i-2] + nums[i]);
    }
    return f[numsSize-1];
}

改进:

因为 在这个状态转移方程里面 有效的数位只有两个;所以我们可以只给数组的长度为 2 来处理

像这样:

int rob(int* nums, int numsSize){
//f[i] = max(f[i-1],f[i-2] + nums[i]);

    int i = 0;
    int f[2] = {0};
    if(numsSize == 2)
        return max(nums[0],nums[1]);
    if(numsSize == 1)
        return nums[0];
    f[0] = nums[0];
    f[1] = max(nums[0],nums[1]);
    for(i = 2;i<numsSize;i++)
    {
        f[i%2] = max(f[(i-1) % 2],f[(i-2)%2] + nums[i]);
    }
    //return max(f[0],f[1]);
    return f[(numsSize - 1) % 2];
}

5. 粉刷房子

问题分析:

这类问题算的上是动态规划类的进阶题了;一排 n 个房子,每个房子可以刷三种颜色;但是相邻的房子不能刷同样的颜色;这就意为着每一个步骤都有很多不同的选择,求最优解的问题,我们用动态规划来解;

状态转移方程:

要刷 n 个房子,而我们每一步最多只能考虑一个房子的选择;大问题化小问题 计算标号为 i 的房子的成本 那么 假设 粉刷标号为 i 的房子为红色的成本为 r ( i )  应该等于 第 i - 1 个 房子 粉刷 绿色 或蓝色的最小值 加上 第 i 个房子刷成红色的成本,写成动态转移方程:

r ( i ) = min ( g( i - 1) ,b ( i - 1) ) + cost [ i ] [ 0 ] ; 

b ( i ) = min ( r( i - 1) ,g ( i - 1) ) + cost [ i ] [ 1 ] ;   

g ( i ) = min ( r( i - 1) ,b ( i - 1) ) + cost [ i ] [ 2 ] ; 

代码实现:

int min(int x,int y)
{
    return x > y ? y : x; 
}

int minCost(int** costs, int costsSize, int* costsColSize){
// 动态规划:
// 1.设计状态 2.写出状态转移方程 3.设定初始状态 4.执行状态转移
// 5.返回最终解;
    if(costsSize == 0)
    return 0;
    else
    {
        int r[2] = {0};
        int g[2] = {0};
        int b[2] = {0};
        r[0] = costs[0][0];
        b[0] = costs[0][1];
        g[0] = costs[0][2];

        int i = 0;
        for(i = 1;i<costsSize;i++)
        {
            r[i % 2] = min(g[(i-1) % 2],b[(i-1) % 2]) + costs[i][0];
            b[i % 2] = min(g[(i-1) % 2],r[(i-1) % 2]) + costs[i][1];
            g[i % 2] = min(r[(i-1) % 2],b[(i-1) % 2]) + costs[i][2];
        }
        i--;
        int D = min(r[i % 2],g[i % 2]);
        return min( D, b[i % 2] );
    }

}
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
数据结构算法 排序算法 内排序 八大基础排序 选择排序 简选择排序 思想 每次选择最大的数插入到末尾中 做法 外层for循环控制次数 内层for循环找出最大的值的角标 找出最大角标后,进行交换 优化思路 同时获取最大值和最小值,然后分别插入数组的首部和尾部 堆排序 思想 使用大顶堆的思想来排序,每次建堆后交换 做法 总体:建堆-替换 建堆 只要左子树或右子树大于当前根节点,则替换 替换后会导致下面的子树发生了变化,因此同样需要进行比较,直至各个节点实现父>子这么一个条件(递归) 交换排序 冒泡排序 思想 每次俩俩交换,将最大值交换到末尾 做法 外层for控制循环次数 内层for控制比较次数 每次循环之后,比较次数都会减少一次 优化思路 如果一趟排序后没有发生位置变化,那么此时就是有序了 快速排序 思想 每次将比支点小的放在支点左边,比支点大的放在支点右边 做法 外循环while只要i和j不碰撞查找 内层两个while循环分别查找出比支点小的和比支点大的角标 如果i<=j满足条件,就交换 一趟排序后,支点的左边都比支点小,支点右边都比支点大 只要满足L<j,i0的条件查找出要插入的何时位置 退出内层while循环后就找到了合适的位置插入 优化思路 二分查找插入,找合适位置的时候使用二分查找算法 希尔排序 思想 用增量来将数组进行分隔,直到增量为1。底层干的还是插入排序干的活 做法 最外层for外循环控制增量的数量,每次/2 第二层for循环控制每次增量那组开始进行插入排序,直至完毕 第三层while循环找到要插入到哪个位置 归并排序 思想 将两个已排好序的数组合并成一个有序的数组 做法 递归拆分出两个有序的数组,从mid的位置开始拆分,递归出口:只有一个值的时候就不用拆分了 合并两个有序的数据 分别往两个数组填充已有序的数据 比较两个数组的值谁小,谁小就放到我们的数组中 如果比较完之后还有剩余的数据,那么用while直接添加到我们的总数组中 优化思路 当递归到规模足够小时,利用插入排序 归并前判断一下是否还有必要归并 只在排序前开辟一次空间 基数(桶)排序 思想 分配,回收(分配到不同的位置上,然后回收)..不断分配..回收来进行排序,直到有序 做法 分配一个[array.length][10列]的二维数组来装我们的元素 最外层for循环控制要分配和回收的次数(根据最大值) 将元素的个、十、百位依次放到桶子上(第一次就是放个位,第二次放十位) 依据每列回收桶子,两个for循环 外排序 查找算法 二分查找 分块查找 哈希查找 贪心算法 求最小生成树的Prim算法和Kruskal算法 爬山问题 回溯算法 n皇后问题 动态规划Dynamic Planning 应用 求最长公共子序列LCS 矩阵连乘问题 爬楼梯问题 找零问题 0-1背包问题 分治算法Divide and Conquer 应用:归并排序 其它 Rabin fingerprints 文件指纹算法 BitMap 位图算法 BloomFilter 布隆过

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值