赞
踩
动态规划是一种解决多阶段决策问题的优化方法,把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。
(1)逆序解法
(2)顺序解法
采用动态规划求解的问题的一般要具有3个性质:
实际应用中简化的步骤:
① 分析最优解的性质,并刻画其结构特征。
② 递归的定义最优解。
③ 以自底向上或自顶向下的记忆化方式计算出最优值。
④ 根据计算最优值时得到的信息,构造问题的最优解。
①动态规划的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。
在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
②动态规划方法又和贪心法有些相似,在动态规划中,可将一个问题的解决方案视为一系列决策的结果。
不同的是,在贪心法中,每采用一次贪心准则便做出一个不可回溯的决策,还要考察每个最优决策序列中是否包含一个最优子序列。
【问题描述】求将正整数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; else dp[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 }
【问题描述】给定一个有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(); }
输入n的值,求得并输出第n个fibonacci数列中的数值
分析
f(1)=1
f(2)=1
f(3)=f(1)+f(2)
f(4)=f(2)+f(3)
如果n<=2
f(n)=1
否则
f(n)=f(n-2)+f(n-1)
低吗如下:
#include<stdio.h> int f(int n) { int y; if(n<=2) y=1; else y=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; else memo[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; else return b; } int main() { int n,y; scanf("%d",&n); y=f(6); printf("%d\n",y); return 0; }
输入一个正整数给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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。