赞
踩
以斐波那契数列为例,它的第一项为1,第二项为1,从第三项开始,每一项的值都是前面两项的和。让我们求第n项的是多少。对于这个问题,我们从最开始的递归思想来看。
- int fib(int n)
- {
- if (n == 1 || n == 2) return 1;
- return fib(n - 1) + fib(n - 2);
- }
这个递归的时间复杂度是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表示还没有计算过
- int dp[20];
- int fib(int n)
- {
- if (n == 1 || n == 2) return 1;
- if (dp[n]) return dp[n];//计算过,直接返回dp[n]
- return dp[n] = fib(n - 1) + fib (n - 2);
- }
使用数组储存状态是动态规划必备的要素。
动态规划问题解决的基本思想:
1、根绝问题所求的那一项和变量的个数,确定是一维数组,二维数组或者多维数组。
2、写出初始值,一般是某个变量为1或者0 的特殊情况时候的解。
3、通过循环,一般是两个循环中间每一层循环表示一个变量的递增情况。
4、在循环中间写出对应的递推关系表达式,求出问题的每一项。
5、根据题意得到答案,一般是数组的最后一项。
1、01背包问题https://www.lanqiao.cn/problems/1174/learning/
- #include <stdio.h>
- #define max(x,y) (x>y?x:y)
- int main(void)
- {
- int n,V;
- scanf("%d %d",&n,&V);
- int w[105]={0},v[105]={0};//分别储存重量和价值的数组
- int dp[105][1005]={0};
- for(int i=1;i<=n;i++)
- scanf("%d %d",&w[i],&v[i]);
- for(int i=1;i<=n;i++)//商品数量上限是n
- for(int j=1;j<=V;j++)//物品体积和不能超过背包容量V
- {
- if(j<w[i])
- dp[i][j]=dp[i-1][j];
- dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
- }
- printf("%d",dp[n][V]);
- return 0;
- }
该情况下的递推关系,x不选,v选该物品
- 5 10
- w v数组
- 1 6
- 2 5
- 3 8
- 5 15
- 3 3
- dp数组
- 0 0 0 0 0 0 0 0 0 0 0
- 0 6 6 6 6 6 6 6 6 6 6
- 0 6 6 11 11 11 11 11 11 11 11
- 0 6 6 11 14 14 19 19 19 19 19
- 0 6 6 11 14 15 21 21 26 29 29
- 0 6 6 11 14 15 21 21 26 29 29
- 29
2、最长上升子序列
https://www.dotcpp.com/oj/problem1557.html
- #include <stdio.h>
- #define max(x,y) (x>y?x:y)
- int main()
- {
- int n,m=0;
- int a[1000]={0};//储存原数组
- int dp[1000]={0};//储存从不同位置开始最长长度
- scanf("%d",&n);
- for(int i=1;i<=n;i++)
- scanf("%d",&a[i]);
-
- for(int i=1;i<=n;i++)
- {
- dp[i]=1;
- for(int j=1;j<i;j++)
- {
- if(a[j]<=a[i])
- dp[i]=max(dp[j]+1,dp[i]);
- }
- m=max(m,dp[i]);
- printf("%d ",m);
- }
- printf("\n%d",m);
- return 0;
- }
思路分析:因为最短也是1,所以说dp数组赋值为1,
从第一个数开始,依次找到以该数为尾的数组的最长上升子序列长度储存在dp数组中
内循环中,依次从第一个数开始遍历,找到比a[i]小的a[j],并且dp[j]最大的数,然后+1赋值给dp[i].
其方程为dp[i]=max(dp[j])+1,下面为输出结果,第三行为dp数组
- 10
- 3 18 7 14 10 12 23 41 16 16
- 1 2 2 3 3 4 5 6 6 6
- 6
3、数字三角形
https://www.lanqiao.cn/problems/505/learning/
- #include<stdio.h>
- #include <stdlib.h>
- #define max(x,y) (x>y?x:y)
-
- int main()
- {
- int n;
- scanf("%d", &n);
- int An[n][n];//储存三角形每个位置的值
-
- for(int i = 0; i < n; i ++)//三角形的行i的循环
- {
- for(int j = 0; j < i + 1; j ++)//三角形列j的循环,列最大等于该行的行数
- scanf("%d", &An[i][j]);
- }
- for(int i = 0; i < n; i ++)//三角形的行i的循环
- {
- for(int j = 0; j < i + 1; j ++)//三角形列j的循环,列最大等于该行的行数
- {
- if(i >= 1)//不是第一列只有一个数的情况下
- {
- if(j == 0)//数组第一列没有左上角的值,直接加上面的值就是最大值
- An[i][j] += An[i -1][j];
- else if(j == i)//即数组每列最后一个没有正上方的值,直接加左上值即可
- An[i][j] += An[i - 1][j - 1];
- else//其余情况加其左上右上的最大值
- {
- int max1 =Max(An[i - 1][j - 1],An[i - 1][j]);
- An[i][j] += max1;
- }
- }
- }
- }
- //上面执行完后,An数组每个值表示顶点到该位置路程最大值
- //向左下走与向右下走的次数相差不能超过 1,即输出第n行中间值,分行数的奇偶
- if(n%2==1)
- printf("%d\n",An[n-1][(n-1)/2]);
- else
- printf("%d\n",Max(An[n-1][(n-1)/2],An[n-1][(n-1)/2+1]));
- return 0;
- }
思路分析:从顶点开始,遍历输入的二维数组,将顶点到该点路径最大值重新给数组该点赋值,题目中说向左下走的次数与向右下走的次数相差不能超过 1。即规定终点只能是最后一排的中间(最后一行为奇数则最中间,偶数则中间两个都可)。
顶点的值就是该点的值,所以说未讨论,如果是像上面几个题一样重新开创新数组顶点要单独赋值,注意本题是将三角形全部靠左对齐,所以说左边一列无左上角值,右边一列无右上角值的下标和原三角形比起来略有差异。
4、跳跃
https://www.lanqiao.cn/problems/553/learning/
- #include <stdio.h>
- int b[105][105];//每个点的权值
- int find_max(int x,int y){
- int max=0;
- for(int i=x;i>=1;i--){//从下往上倒推
- for(int j=y;j>=1;j--){//从右往左倒推
- if(!(x==i&&y==j)&&(x-i+y-j)<=3&&max<b[i][j]){
- //同时满足三个条件
- //1.不原地踏步2.每次跳跃在距离范围之内3.比当前max更大时才更新max
- max=b[i][j];
- }
- }
- }
- return max;
- }
- int main(){
- int n,m;
- scanf("%d %d",&n,&m);
- int i,j;
- for(i=1;i<=n;i++){
- for(j=1;j<=m;j++)
- scanf("%d",&b[i][j]);
- }
- for(i=1;i<=n;i++){
- for(j=1;j<=m;j++)
- b[i][j]+=find_max(i,j);
- }
- printf("%d",b[n][m]+b[1][1]);
- }
思路分析:第三题的衍生题,三角形变为矩阵,每次移动的选择更多,但思路相同,都需要找出顶点到每个点路径的最大值,并且储存在数组中。最后输出数组终点的值即可。
数字三角形是本身加上旁边两个相邻位置的最大值,此处也一样,一个点能到的是其右下角半径<=3的圆内的点,所以说一个点的值应该是该点值加上其左上角扇形内的最大值。所以说在find-max函数内是倒序遍历
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。