算法设计与分析-----动态规划
算法设计与分析-----动态规划(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;
}
算法设计与分析-----动态规划相关推荐
- 算法设计与分析——动态规划(二):钢条切割
分类目录:<算法设计与分析>总目录 相关文章: · 动态规划(一):基础知识 · 动态规划(二):钢条切割 · 动态规划(三):矩阵链乘法 · 动态规划(四):动态规划详解 · 动态规划( ...
- 算法设计与分析——动态规划(五):最长公共子序列
分类目录:<算法设计与分析>总目录 相关文章: · 动态规划(一):基础知识 · 动态规划(二):钢条切割 · 动态规划(三):矩阵链乘法 · 动态规划(四):动态规划详解 · 动态规划( ...
- 计算机算法设计与分析 动态规划 实验报告,动态规划法解最长公共子序列(计算机算法设计与分析实验报告).doc...
动态规划法解最长公共子序列(计算机算法设计与分析实验报告) 实报 告 实验名称:任课教师::姓 名:完成日期:二.主要实验内容及要求: 要求按动态规划法原理求解问题: 要求交互输入两个序列数据: 要求 ...
- 算法设计与分析——动态规划(一)矩阵连乘
动态规划--Dynamic programming,可以说是本人一直没有啃下的骨头,这次我就得好好来学学Dynamic programming. OK,出发! 动态规划通常是分治算法的一种特殊情况,它 ...
- 算法设计与分析——动态规划——数字三角形问题
数字三角形问题 1.题目描述:给定一个由n行数字组成的数字三角形,如图3-7所示.设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大. 算法设计:对于给定的由n行数字组成的数字 ...
- 算法设计与分析—动态规划算法
动态规划算法 1.动态规划算法基本思想 2.动态规划算法求解步骤 3. 0-1背包问题 在现实生活中,存在这样一类问题,它们的活动过程不仅可以分成若干阶段,而且在任意一个阶段(不妨设为第i个阶段)以后 ...
- 算法设计与分析——动态规划——矩阵连乘问题
动态规划与分治法的异同: 相同点:其基本思想都是将待求解问题分解为若干子问题,先求解子问题,再结合这些子问题的解得到原问题的解. 差异点:与分治法不同的是,适合用动态规划法求解的问题经分解得到的子问题 ...
- 算法设计与分析——动态规划——石子合并问题
1.石子合并问题 在一个圆形操场的四周摆放着n堆石子.现要将石子有序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分.设计一个算法,计算出将n堆石子合并成 ...
- 算法设计与分析——动态规划——01背包问题
#include<iostream> #include<iomanip> using namespace std; //前i个物品装入容量为j的背包中获得的最大价值//0-1背 ...
最新文章
- javascript之namespace模式
- Matlab的size()函数
- python开发工具和框架安装器_Python 开发工具和框架安装
- 基类和派生类中使用static_cast和dynamic_cast进行类型转换
- 【错误记录】Android Studio 编译时 Kotlin 代码编译报错 ( Not enough information to infer type variable T )
- JavasSript实现秒转换为“天时分秒”控件和TDD测试方法应用
- Vue实现仿音乐播放器项目总述以及阶段目录
- VMware虚拟机中调用本机摄像头详解
- LCD1602,4位数据总线液晶屏时钟,STC12C5A60S2的10位ADC功能程序
- [css] 如何使用css实现鼠标跟随?
- 用python画机器猫代码_如何用Python画一只机器猫?| 原力计划
- 【01】blockqote美化
- Oracle中备用查询语句
- java 计算年龄_Java 根据出生日期计算年龄
- matlab 跳步循环,足球训练:每天10分钟挑战7天球感训练
- 专升本英语——语法知识——高频语法——第四节 定语从句(限制性定语从句-非限制性定语从句)【学习笔记】
- jupter 使用
- MyBatis-Plus代码生成器(新)3.5.2的使用
- rgb格式颜色与#000000十六进制格式颜色的转换原理
- 被动信息收集----指纹识别(CMS识别)