动态规划问题(1)

动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。

视频教程上以斐波那契数列为例,教先递归(复杂度指数增长),再找重复计算的部分,替换为记忆搜索法(自顶向下,复杂度O(2n-1)),再转换为自底向上的动态规划(复杂度O(n));但是自我感觉看下面例题程序更为易懂,就是从下向上或者从左向右的递推。

  1. leetcode 第120题 Triangle
    给定一个三角形数字阵列,选择一条自顶向下的路径,使得沿途所有数字之和最小,每一步只能移动到相邻的格子中。
    [2],
    [3,4],
    [6,5,7],
    [4,1,8,3]
    例子最小值为11(i.e., 2 + 3 + 5 + 1 = 11). Note: 设定计算复杂度为O(n) .
class Solution {
public:
    int minimumTotal(vector<vector<int> > &triangle) {
        int i,j;
        for(i=triangle.size()-2;i>=0;i--)//vector下标从0开始,所以对于4行的,第三行的下标为2
        {
            for(j=0;j<i+1;j++)
            {
                triangle[i][j]+=min(triangle[i+1][j],triangle[i+1][j+1]);
            }
        }
        return triangle[0][0];
    }
};
//金字塔自下向上,逐层递推上去
  1. 第64题 Minimum path sum
    给一个m*n矩阵,其中每一个格子包含一个非负整数,寻找一条从左上角到右下角的路线,使得沿路的数字和最小。每一步只能左移或者下移。
class Solution {
public:
    int minPathSum(vector<vector<int> > &grid) {
        int m=grid.size();
        int n=grid[0].size();
        for(int i = 0; i < m; i++)
        {
            for(int j=0;j<n;j++)
            {
                if(i==0&&j!=0) grid[i][j]+=grid[i][j-1];  //第一行的值没有上方值,只加左方值更新自己
                if(i!=0&&j==0) grid[i][j]+=grid[i-1][j];  //第一列的值没有左方值,只加上方值更新自己
                if(i!=0&&j!=0)
                {
                    grid[i][j]+=min(grid[i][j-1],grid[i-1][j]);
                }
            }
        }
        return grid[m-1][n-1];
    }
};
//从左向右更新矩阵,每个位置的值等于 原值+上方或左方的较小值。
  1. (leetcode343)Integer Break
    给定一个正数n,可以将其分割成多个数字的和,若要让这些数字的乘积最大,求分割方法(至少要分成两个数)。算法返回这个最大的乘积。
    如n=2,返回1,2=1+1
    n=10,返回36, 10=3+3+4
    (1) 递归法(自顶向下,确定出树的结构)
    在这里插入图片描述
#include <iostream>
using namespace std;
int ibreak(int n){
	int product =0;
	if ( n == 1 ) return 1;
	if ( n == 2 ) return 2;
	for ( int i=1; i<n; i++)
	{
	   //注意得比较上i*(n-i),以决定是否进一步划分下去,
		product = max( max(i*(n-i), i*ibreak(n-i)), product); 
	}
	return product;
}
int main() {
	int n = 2;
	cout << ibreak(n) <<endl;
	return 0;
}

(2)记忆化搜索的方法
将递归中的重复计算的值,存到一个动态数组里,然后将递归转化为记忆化搜索。实际上就是添加库,定义、初始化动态数组,按照递归过程为动态数组赋值。

#include <iostream>
#include <cassert>
#include<vector>
using namespace std;
class Solution{
public:
 int ibreak(int n){
   	vector<int> memo;
   	memo = vector<int> (n+1,-1);
	if ( n == 1 ) return 1;
	if ( n == 2 ) return 2;
	if ( memo[n] != -1)    //当已计算过memo[n],直接返回memo值
	   return memo[n];
	int product=0;   
	for ( int i=1; i<n; i++)
	{
		product = max( max(i*(n-i), i*ibreak(n-i)), product);
	}
	memo[n] = product;
	return memo[n];
  }
};
int main() {
	int n = 10;
	Solution a;    //调用类内函数,需要先定义类的对象
	cout <<a.ibreak(n)<<endl;
	return 0;
}

(3)动态规划的解法(双重循环)
自底向上,先计算最底层的,递推上去。所以先计算n=1时,然后使用双层循环为memo【i】赋值,计算到n。

#include <iostream>
#include <cassert>
#include<vector>
using namespace std;
class Solution{
public:
 int ibreak(int n){
   	vector<int> memo;
   	memo = vector<int> (n+1,-1);
   	//动态规划为自底向上递推,先计算n=1
	memo[1]=1;
	for( int i=2; i<=n; i++){
		//求解memo[i]
		for( int j=1; j<i; j++){
			//每一个i=j+(i-j)
			memo[i] = max(memo[i],max(j*memo[i-j],j*(i-j)));
		}
	}
	return memo[n];
  }
};
int main() {
	int n = 10;
	Solution a;    //调用类内函数,需要先定义类的对象
	cout <<a.ibreak(n)<<endl;
	return 0;
}
  1. (279) Perfect Squares
    给出一个正整数n,寻找最少的完全平方数,使他们的和为n。完全平方数:1,4,9,16…, 12=4+4+4, 输出3;13=4+9,输出2
    (1)递归法
    n
    n-1 n-4 …
    n-2 n-5 n-5 n-8 … 每一个n下一步可以分为 1+(n-1), 4+(n-4), …t^2 + (n-t方)共t种可能,可以使用递归逐步向下
#include <iostream>
#include <cassert>
#include <cmath>
using namespace std;
class Solution{
public:
 int PSquare(int n){
 	int t = floor(sqrt(n));
 	int num = 65536;
 	if (n == 0) return 0;
 	if (n == 1) return 1;
 	for(int i = 1; i <= t; i++)
 	{
     	//n分解为,1个1和PSquare(n-1), || 1个4和PSquare(n-4) ||....依次遍历保留最小
 		num = min (num, 1+PSquare(n-pow(i,2))); 
 	}
	return num;
  }
};
int main() {
	int n = 12;
	Solution a;    
	cout <<a.PSquare(n)<<endl;
	return 0;
}

(2)记忆化搜索法
在递归的基础上,将num改为memo[], 循环前加一个判断,是不是计算过

#include <iostream>
#include <cassert>
#include <cmath>
#include<vector>

using namespace std;
class Solution{
public:
 int PSquare(int n){
 	vector <int> memo;
 	memo = vector<int> (n+1,65536);
 	
 	int t = floor(sqrt(n));
 //	int num = 65536;
 	if (n == 0) memo[n]=0;
 	if (n == 1) memo[n]=1;
 	if (memo[n] != 65536) return memo[n];
 	for(int i = 1; i <= t; i++)
 	{
 		memo[n] = min (memo[n], 1+PSquare(n-pow(i,2)));
 	}
	return memo[n];
  }
};
int main() {
	int n = 12;
	Solution a;    //调用类内函数,需要先定义类的对象
	cout <<a.PSquare(n)<<endl;
	return 0;
}

(3)动态规划
在记忆化搜索的基础上,改为自底向上。

#include <iostream>
#include <cassert>
#include <cmath>
#include<vector>

using namespace std;
class Solution{
public:
 int PSquare(int n){
 	vector <int> memo;
 	memo = vector<int> (n+1,65536);
 	
 	if (n == 0) memo[n]=0;
 	if (n == 1) memo[n]=1;
 	if (memo[n] != 65536) return memo[n];
 	for(int i = 2; i <= n; i++)  //动态规划就是在外层再加一个外循环,求n改为求i,别忘了把t移进来
 	{
 		//求memo[i]
 		int t = floor(sqrt(i));
 		for(int j=1; j<=t; j++)
 		{
 			memo[i] = min (memo[i], 1+PSquare(i-pow(j,2)));
 		}
 	}
	return memo[n];
  }
};
int main() {
	int n = 12;
	Solution a;    
	cout <<a.PSquare(n)<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值