当前位置:   article > 正文

动态规划(C语言)_c语言动态规划算法

c语言动态规划算法

一、入门

 以斐波那契数列为例,它的第一项为1,第二项为1,从第三项开始,每一项的值都是前面两项的和。让我们求第n项的是多少。对于这个问题,我们从最开始的递归思想来看。

  1. int fib(int n)
  2. {
  3. if (n == 1 || n == 2) return 1;
  4. return fib(n - 1) + fib(n - 2);
  5. }

这个递归的时间复杂度是O(2^n),随着n的增大,呈指数级别增长。递归的时间复杂度之所以高,是因为它涉及了很多重复性计算。比如在计算fib(5)的时候,fib(5) = fib(4) + fib(3)。接下来计算fib(4),fib(4) = fib(3) + fib(2)。fib(4)计算完后就会再计算fib(3)。然而我们之前在计算fib(4)的时候已经算出fib(3)了。fib(3)被重复计算了两次。当n很大的时候。重复计算的工作就越来越多。

为了避免重复计算,我们可以在计算的过程中将已经计算过的值保存下来,等到再一次遇到的时候就可以直接使用。开一个dp[]数组,dp[i]存储fib(i)的值。dp[i] = 0表示还没有计算过

  1. int dp[20];
  2. int fib(int n)
  3. {
  4. if (n == 1 || n == 2) return 1;
  5. if (dp[n]) return dp[n];//计算过,直接返回dp[n]
  6. return dp[n] = fib(n - 1) + fib (n - 2);
  7. }

使用数组储存状态是动态规划必备的要素。

动态规划问题解决的基本思想:

1、根绝问题所求的那一项和变量的个数,确定是一维数组,二维数组或者多维数组。

2、写出初始值,一般是某个变量为1或者0 的特殊情况时候的解。

3、通过循环,一般是两个循环中间每一层循环表示一个变量的递增情况。

4、在循环中间写出对应的递推关系表达式,求出问题的每一项。

5、根据题意得到答案,一般是数组的最后一项。

二、DP思想入门

1、01背包问题https://www.lanqiao.cn/problems/1174/learning/

  1. #include <stdio.h>
  2. #define max(x,y) (x>y?x:y)
  3. int main(void)
  4. {
  5. int n,V;
  6. scanf("%d %d",&n,&V);
  7. int w[105]={0},v[105]={0};//分别储存重量和价值的数组
  8. int dp[105][1005]={0};
  9. for(int i=1;i<=n;i++)
  10. scanf("%d %d",&w[i],&v[i]);
  11. for(int i=1;i<=n;i++)//商品数量上限是n
  12. for(int j=1;j<=V;j++)//物品体积和不能超过背包容量V
  13. {
  14. if(j<w[i])
  15. dp[i][j]=dp[i-1][j];
  16. dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
  17. }
  18. printf("%d",dp[n][V]);
  19. return 0;
  20. }

该情况下的递推关系,x不选,v选该物品

 

  1. 5 10
  2. w v数组
  3. 1 6
  4. 2 5
  5. 3 8
  6. 5 15
  7. 3 3
  8. dp数组
  9. 0 0 0 0 0 0 0 0 0 0 0
  10. 0 6 6 6 6 6 6 6 6 6 6
  11. 0 6 6 11 11 11 11 11 11 11 11
  12. 0 6 6 11 14 14 19 19 19 19 19
  13. 0 6 6 11 14 15 21 21 26 29 29
  14. 0 6 6 11 14 15 21 21 26 29 29
  15. 29

2、最长上升子序列

https://www.dotcpp.com/oj/problem1557.html

  1. #include <stdio.h>
  2. #define max(x,y) (x>y?x:y)
  3. int main()
  4. {
  5. int n,m=0;
  6. int a[1000]={0};//储存原数组
  7. int dp[1000]={0};//储存从不同位置开始最长长度
  8. scanf("%d",&n);
  9. for(int i=1;i<=n;i++)
  10. scanf("%d",&a[i]);
  11. for(int i=1;i<=n;i++)
  12. {
  13. dp[i]=1;
  14. for(int j=1;j<i;j++)
  15. {
  16. if(a[j]<=a[i])
  17. dp[i]=max(dp[j]+1,dp[i]);
  18. }
  19. m=max(m,dp[i]);
  20. printf("%d ",m);
  21. }
  22. printf("\n%d",m);
  23. return 0;
  24. }

思路分析:因为最短也是1,所以说dp数组赋值为1,

从第一个数开始,依次找到以该数为尾的数组的最长上升子序列长度储存在dp数组中

内循环中,依次从第一个数开始遍历,找到比a[i]小的a[j],并且dp[j]最大的数,然后+1赋值给dp[i].

其方程为dp[i]=max(dp[j])+1,下面为输出结果,第三行为dp数组

  1. 10
  2. 3 18 7 14 10 12 23 41 16 16
  3. 1 2 2 3 3 4 5 6 6 6
  4. 6

3、数字三角形

https://www.lanqiao.cn/problems/505/learning/

  1. #include<stdio.h>
  2. #include <stdlib.h>
  3. #define max(x,y) (x>y?x:y)
  4. int main()
  5. {
  6. int n;
  7. scanf("%d", &n);
  8. int An[n][n];//储存三角形每个位置的值
  9. for(int i = 0; i < n; i ++)//三角形的行i的循环
  10. {
  11. for(int j = 0; j < i + 1; j ++)//三角形列j的循环,列最大等于该行的行数
  12. scanf("%d", &An[i][j]);
  13. }
  14. for(int i = 0; i < n; i ++)//三角形的行i的循环
  15. {
  16. for(int j = 0; j < i + 1; j ++)//三角形列j的循环,列最大等于该行的行数
  17. {
  18. if(i >= 1)//不是第一列只有一个数的情况下
  19. {
  20. if(j == 0)//数组第一列没有左上角的值,直接加上面的值就是最大值
  21. An[i][j] += An[i -1][j];
  22. else if(j == i)//即数组每列最后一个没有正上方的值,直接加左上值即可
  23. An[i][j] += An[i - 1][j - 1];
  24. else//其余情况加其左上右上的最大值
  25. {
  26. int max1 =Max(An[i - 1][j - 1],An[i - 1][j]);
  27. An[i][j] += max1;
  28. }
  29. }
  30. }
  31. }
  32. //上面执行完后,An数组每个值表示顶点到该位置路程最大值
  33. //向左下走与向右下走的次数相差不能超过 1,即输出第n行中间值,分行数的奇偶
  34. if(n%2==1)
  35. printf("%d\n",An[n-1][(n-1)/2]);
  36. else
  37. printf("%d\n",Max(An[n-1][(n-1)/2],An[n-1][(n-1)/2+1]));
  38. return 0;
  39. }

思路分析:从顶点开始,遍历输入的二维数组,将顶点到该点路径最大值重新给数组该点赋值,题目中说向左下走的次数与向右下走的次数相差不能超过 1。即规定终点只能是最后一排的中间(最后一行为奇数则最中间,偶数则中间两个都可)。

顶点的值就是该点的值,所以说未讨论,如果是像上面几个题一样重新开创新数组顶点要单独赋值,注意本题是将三角形全部靠左对齐,所以说左边一列无左上角值,右边一列无右上角值的下标和原三角形比起来略有差异。

4、跳跃

https://www.lanqiao.cn/problems/553/learning/

  1. #include <stdio.h>
  2. int b[105][105];//每个点的权值
  3. int find_max(int x,int y){
  4. int max=0;
  5. for(int i=x;i>=1;i--){//从下往上倒推
  6. for(int j=y;j>=1;j--){//从右往左倒推
  7. if(!(x==i&&y==j)&&(x-i+y-j)<=3&&max<b[i][j]){
  8. //同时满足三个条件
  9. //1.不原地踏步2.每次跳跃在距离范围之内3.比当前max更大时才更新max
  10. max=b[i][j];
  11. }
  12. }
  13. }
  14. return max;
  15. }
  16. int main(){
  17. int n,m;
  18. scanf("%d %d",&n,&m);
  19. int i,j;
  20. for(i=1;i<=n;i++){
  21. for(j=1;j<=m;j++)
  22. scanf("%d",&b[i][j]);
  23. }
  24. for(i=1;i<=n;i++){
  25. for(j=1;j<=m;j++)
  26. b[i][j]+=find_max(i,j);
  27. }
  28. printf("%d",b[n][m]+b[1][1]);
  29. }

思路分析:第三题的衍生题,三角形变为矩阵,每次移动的选择更多,但思路相同,都需要找出顶点到每个点路径的最大值,并且储存在数组中。最后输出数组终点的值即可。

数字三角形是本身加上旁边两个相邻位置的最大值,此处也一样,一个点能到的是其右下角半径<=3的圆内的点,所以说一个点的值应该是该点值加上其左上角扇形内的最大值。所以说在find-max函数内是倒序遍历

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/87194
推荐阅读
相关标签
  

闽ICP备14008679号