算法设计与分析-----动态规划(c语言)

  • 一、动态规划
    • 1、定义
    • 2、动态规划问题的解法
    • 3、动态规划求解的基本步骤
    • 4、动态规划与其他方法的比较
    • 5、求解整数拆分问题
    • 6、求解最大连续子序列和问题
  • 二、动态规划算法实验
    • 1、实验一 fibonacci数列
    • 2、实验二 拆分方案的个数的求解。

一、动态规划

1、定义

动态规划是一种解决多阶段决策问题的优化方法,把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。

2、动态规划问题的解法

(1)逆序解法

(2)顺序解法

3、动态规划求解的基本步骤

采用动态规划求解的问题的一般要具有3个性质:

  • **最优性原理:**如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优性原理。
  • 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
  • 有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)。

实际应用中简化的步骤:

① 分析最优解的性质,并刻画其结构特征。

② 递归的定义最优解。

③ 以自底向上或自顶向下的记忆化方式计算出最优值。

④ 根据计算最优值时得到的信息,构造问题的最优解。

4、动态规划与其他方法的比较

​ ①动态规划的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。

在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

​ ②动态规划方法又和贪心法有些相似,在动态规划中,可将一个问题的解决方案视为一系列决策的结果。

不同的是,在贪心法中,每采用一次贪心准则便做出一个不可回溯的决策,还要考察每个最优决策序列中是否包含一个最优子序列。

5、求解整数拆分问题

问题描述】求将正整数n无序拆分成最大数为k(称为n的k拆分)的拆分方案个数,要求所有的拆分方案不重复。

问题求解】设n=5,k=5,对应的拆分方案有:

① 5=5

② 5=4+1

③ 5=3+2

④ 5=3+1+1

⑤ 5=2+2+1

⑥ 5=1+1+1+1

⑦ 5=1+1+1+1+1

为了防止重复计数,让拆分数保持从大到小排序。正整数5的拆分数为7。

采用动态规划求解整数拆分问题。设f(n,k)为n的k拆分的拆分方案个数:

(1)当n=1,k=1时,显然f(n,k)=1。

(2)当n<k时,有f(n,k)=f(n,n)。

(3)当n=k时,其拆分方案有将正整数n无序拆分成最大数为n-1的拆分方案,以及将n拆分成1个n(n=n)的拆分方案,后者仅仅一种,所以有

​ f(n,n)=f(n,n-1)+1。

(4)当n>k时,根据拆分方案中是否包含k,可以分为两种情况:

​ ① 拆分中包含k的情况:即一部分为单个k,另外一部分为{x1,x2,…,xi},后者的和为n-k,后者中可能再次出现k,因此是(n-k)的k拆分,所以这种拆分方案个数为f(n-k,k)。

​ ② 拆分中不包含k的情况:则拆分中所有拆分数都比k小,即n的(k-1)拆分,拆分方案个数为f(n,k-1)。

代码如下:

#include <stdio.h>
#define MAXN 500
int dp[MAXN][MAXN];         //动态规划数组
void Split(int n,int k)     //求解算法
{for (int i=1;i<=n;i++)for(int j=1;j<=k;j++){if (i==1 || j==1)dp[i][j]=1;else if (i<j)dp[i][j]=dp[i][i];else if (i==j)dp[i][j]=dp[i][j-1]+1;elsedp[i][j]=dp[i][j-1]+dp[i-j][j];}
}
int main()
{int n=5,k=5;Split(n,k);printf("(%d,%d)=%d\n",n,k,dp[n][k]);   //输出:7
}

6、求解最大连续子序列和问题

问题描述】给定一个有n(n≥1)个整数的序列,要求求出其中最大连续子序列的和。

例如

序列(-2,11,-4,13,-5,-2)的最大子序列和为20

序列(-6,2,4,-7,5,3,2,-1,6,-9,10,-2)的最大子序列和为16

规定一个序列最大连续子序列和至少是0,如果小于0,其结果为0。

问题求解】对于含有n个整数的序列a,设

bj=MAX{ai+ai+1+…+aj} (1≤j≤n)

表示a[1…j]的前j个元素中的最大连续子序列和,则bj-1表示a[1…j-1]的前j-1个元素中的最大连续子序列和。

当bj-1>0时,bj=bj-1+aj,当bj-1≤0时,放弃前面选取的元素,从aj开始重新选起,bj=aj。用一维动态规划数组dp存放b,对应的状态转移方程如下:

dp[0]=0                 边界条件
dp[j]=MAX{dp[j-1] +aj,aj}      1≤j≤n

则序列a的最大连续子序列和等于dp[j](1≤j≤n)中的最大者。

代码如下:

#include <stdio.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define MAXN 20
//问题表示
int n=6;
int a[]={0,-2,11,-4,13,-5,-2}; //a数组不用下标为0的元素
//求解结果表示
int dp[MAXN];
void maxSubSum()                //求dp数组
{dp[0]=0;for (int j=1;j<=n;j++)dp[j]=max(dp[j-1]+a[j],a[j]);
}
void dispmaxSum()                   //输出结果
{int k; int maxj=1;for (int j=2;j<=n;j++)           //求dp中最大元素dp[maxj]if (dp[j]>dp[maxj]) maxj=j;for (k=maxj;k>=1;k--)     //找前一个值小于等于0者if (dp[k]<=0) break;printf("    最大连续子序列和: %d\n",dp[maxj]);printf("    所选子序列: ");for (int i=k+1;i<=maxj;i++)printf("%d ",a[i]);printf("\n");
}
int main()
{maxSubSum();printf("求解结果\n");dispmaxSum();
}

二、动态规划算法实验

1、实验一 fibonacci数列

输入n的值,求得并输出第n个fibonacci数列中的数值

分析

f(1)=1

f(2)=1

f(3)=f(1)+f(2)

f(4)=f(2)+f(3)

如果n<=2f(n)=1
否则f(n)=f(n-2)+f(n-1)

低吗如下:

#include<stdio.h>
int f(int n)
{int y;if(n<=2)y=1;elsey=f(n-2)+f(n-1);return y;
} int main()
{int n,y;scanf("%d",&n);y=f(n);printf("%d\n",y);return 0;
}

缺点:重复计算,会耗费运行时间。

把每次计算的结果用一个数组来存放!【动态规划解决问题的思路之一】

我们同样会用到动态划分存储空间的操作【动态规划解决问题的思路之一】

改进1:

#include<stdio.h>
//*(memo+i) 等价 memo[i]
int qiu(int n,int *memo)
{if(memo[n]!=-1)return memo[n];if(n<=2)memo[n]=1;elsememo[n]=qiu(n-2,memo)+qiu(n-1,memo);printf("n=%d\n",n);return memo[n];
}int f(int n)
{int i;int memo[n+1];for(i=0;i<=n;i++)memo[i]=-1;return qiu(n,memo);
} int main()
{int n,y;scanf("%d",&n);y=f(n);printf("%d\n",y);return 0;
}

动态规划:为了介绍重复计算!【初衷之一】

动态规划:在解决问题的中尽可能的减少内存的耗费!!!【初衷之二】

改进2:

#include<stdio.h>
#include<stdlib.h>
//*(memo+i) 等价 memo[i]
int f(int n)
{int i,a,b;if(n<=0)return n;a=1;b=1; for(i=3;i<=n;i=i+2){a=a+b;b=a+b;}if(n%2==1)return a;elsereturn b;
} int main()
{int n,y;scanf("%d",&n);y=f(6);printf("%d\n",y);return 0;
}

2、实验二 拆分方案的个数的求解。

输入一个正整数给n,对n进行拆分(且拆分中的最大数值是k),编程求得当对n进行按最大值为k的拆分方案的个数的求解。

分析:

假设n=5,k=5。答案是7种组合

① 5=5

② 5=4+1

③ 5=3+2

④ 5=3+1+1

⑤ 5=2+2+1

⑥ 5=2+1+1+1

⑦ 5=1+1+1+1+1

为了防止重复计算,让拆分数据的分析一直保持一个状态从大到小的排序!

采用动态规划的思想来求解拆分问题,寻找问题求解的规律【方法】

我们准备一个函数f(n,k),对n进行最大值为k的拆分方案个数的求解

详细分析

第1种情况 当n=1的时候 或者 k=1的时候 ,f(n,k)=1

第2种情况 当n<k的时候 例如f(5,10) 等价 f(5,5)

​ f(n,k)=f(n,n)

第3种情况 当n=k的时候 分成2个部分

​ 【一个包含本身,一个不包含本身】

​ f(n,k)=f(n,n-1)+1

​ 【这里最后+1 其实就是下面黄色一种组合】

① 5=5

② 5=4+1

③ 5=3+2

④ 5=3+1+1

⑤ 5=2+2+1

⑥ 5=2+1+1+1

⑦ 5=1+1+1+1+1

第4种情况 当n>k的时候

(1)拆分中包含k的情况

​ f(n-k,k)

(2)拆分中不包含k的情况

​ f(n,k-1)

最后是f(n,k)=f(n-k,k)+f(n,k-1)

低吗如下:

#include<stdio.h>
int f(int n,int k)
{if(n==1 || k==1)return 1;else if(n<k)return f(n,n);else if(n==k)return f(n,n-1)+1;else if(n>k)return f(n-k,k)+f(n,k-1);
}int main()
{int n,k,y;scanf("%d",&n);scanf("%d",&k);y=f(n,k);printf("%d\n",y);return 0;
}

改进:

#include<stdio.h>
int a[100][100];
int f(int n,int k)
{int i,j;for(i=1;i<=n;i++)for(j=1;j<=k;j++){if(i==1 || j==1)a[i][j]=1;else if(i<j)a[i][j]=a[i][i];else if(i==j)a[i][j]=a[i][i-1]+1;else if(i>j)a[i][j]=a[i-j][j]+a[i][j-1];}return a[n][k];
}int main()
{int n,k,y;scanf("%d",&n);scanf("%d",&k);y=f(n,k);printf("%d\n",y);return 0;
}

算法设计与分析-----动态规划相关推荐

  1. 算法设计与分析——动态规划(二):钢条切割

    分类目录:<算法设计与分析>总目录 相关文章: · 动态规划(一):基础知识 · 动态规划(二):钢条切割 · 动态规划(三):矩阵链乘法 · 动态规划(四):动态规划详解 · 动态规划( ...

  2. 算法设计与分析——动态规划(五):最长公共子序列

    分类目录:<算法设计与分析>总目录 相关文章: · 动态规划(一):基础知识 · 动态规划(二):钢条切割 · 动态规划(三):矩阵链乘法 · 动态规划(四):动态规划详解 · 动态规划( ...

  3. 计算机算法设计与分析 动态规划 实验报告,动态规划法解最长公共子序列(计算机算法设计与分析实验报告).doc...

    动态规划法解最长公共子序列(计算机算法设计与分析实验报告) 实报 告 实验名称:任课教师::姓 名:完成日期:二.主要实验内容及要求: 要求按动态规划法原理求解问题: 要求交互输入两个序列数据: 要求 ...

  4. 算法设计与分析——动态规划(一)矩阵连乘

    动态规划--Dynamic programming,可以说是本人一直没有啃下的骨头,这次我就得好好来学学Dynamic programming. OK,出发! 动态规划通常是分治算法的一种特殊情况,它 ...

  5. 算法设计与分析——动态规划——数字三角形问题

    数字三角形问题 1.题目描述:给定一个由n行数字组成的数字三角形,如图3-7所示.设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大. 算法设计:对于给定的由n行数字组成的数字 ...

  6. 算法设计与分析—动态规划算法

    动态规划算法 1.动态规划算法基本思想 2.动态规划算法求解步骤 3. 0-1背包问题 在现实生活中,存在这样一类问题,它们的活动过程不仅可以分成若干阶段,而且在任意一个阶段(不妨设为第i个阶段)以后 ...

  7. 算法设计与分析——动态规划——矩阵连乘问题

    动态规划与分治法的异同: 相同点:其基本思想都是将待求解问题分解为若干子问题,先求解子问题,再结合这些子问题的解得到原问题的解. 差异点:与分治法不同的是,适合用动态规划法求解的问题经分解得到的子问题 ...

  8. 算法设计与分析——动态规划——石子合并问题

    1.石子合并问题 在一个圆形操场的四周摆放着n堆石子.现要将石子有序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分.设计一个算法,计算出将n堆石子合并成 ...

  9. 算法设计与分析——动态规划——01背包问题

    #include<iostream> #include<iomanip> using namespace std; //前i个物品装入容量为j的背包中获得的最大价值//0-1背 ...

最新文章

  1. javascript之namespace模式
  2. Matlab的size()函数
  3. python开发工具和框架安装器_Python 开发工具和框架安装
  4. 基类和派生类中使用static_cast和dynamic_cast进行类型转换
  5. 【错误记录】Android Studio 编译时 Kotlin 代码编译报错 ( Not enough information to infer type variable T )
  6. JavasSript实现秒转换为“天时分秒”控件和TDD测试方法应用
  7. Vue实现仿音乐播放器项目总述以及阶段目录
  8. VMware虚拟机中调用本机摄像头详解
  9. LCD1602,4位数据总线液晶屏时钟,STC12C5A60S2的10位ADC功能程序
  10. [css] 如何使用css实现鼠标跟随?
  11. 用python画机器猫代码_如何用Python画一只机器猫?| 原力计划
  12. 【01】blockqote美化
  13. Oracle中备用查询语句
  14. java 计算年龄_Java 根据出生日期计算年龄
  15. matlab 跳步循环,足球训练:每天10分钟挑战7天球感训练
  16. 专升本英语——语法知识——高频语法——第四节 定语从句(限制性定语从句-非限制性定语从句)【学习笔记】
  17. jupter 使用
  18. MyBatis-Plus代码生成器(新)3.5.2的使用
  19. rgb格式颜色与#000000十六进制格式颜色的转换原理
  20. 被动信息收集----指纹识别(CMS识别)

热门文章

  1. 谷贱伤农,薪贱伤码农!
  2. 解析几何----拉格朗日乘数法在解析几何中的应用
  3. 基于同创logos2的国产FPGA加速器与DMA设计与实现
  4. IP地址(简单模拟)
  5. activiti学习(二)——activiti流程的部署
  6. 关于高可用的系统【转自酷壳】
  7. 【Leetcode刷题】35.搜索插入位置
  8. Cytoskeleton / 艾美捷——泛素化亲和珠
  9. mysql 木马_用Mysql语句来生成后门木马方案
  10. 红米手机4X完美卡刷开发版开启Root超级权限的流程