動態規劃 LeetCode 70&120&64&279&300&322

LeetCode 70 爬樓梯

題目

假設你正在爬樓梯。需要 n階你才能到達樓頂。
每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
注意:給定 n 是一個正整數。
輸入: 2
輸出: 2
解釋: 有兩種方法可以爬到樓頂。
1.  1 階 + 1 階
2.  2 階

思路

F(n)=F(n-1)+F(n-2) 斐波那契數列的應用

代碼

#include <iostream>
#include <vector>
using namespace std;

//動態規劃解 
class Solution {
public:
    int climbStairs(int n) {
    	vector<int> memo;
		memo = vector<int>(n+1,-1);
		memo[1] = 1;
		memo[2] = 2;
		for(int i=3; i<=n; i++)
			memo[i] = memo[i-1] + memo[i-2];
		return memo[n]; 
    }
};
int main()
{
	return 0;
}

LeetCode 120 三角形最小路徑和

題目

給定一個三角形,找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。
相鄰的結點 在這裏指的是 下標 與 上一層結點下標 相同或者等於 上一層結點下標 + 1 的兩個結點。
例如,給定三角形:
[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]
自頂向下的最小路徑和爲11(即,2+3+5+1= 11)。

思路

從倒二行起,從下至上,從左至右,依次更新三角形數組上的值

更新值爲下一層相鄰兩個數和的最小值。那麼triangle[i][j]就表示從點[(i,j)開始自頂向下的最小路徑和

代碼

#include <iostream>
#include <vector>
using namespace std;

//動態規劃解 
class Solution {
public:
    int minimumTotal(vector<vector<int> >& triangle) {
    	int n = triangle.size();
    	if(n==0) return 0;
    	//從倒二行開始更新數組,數組更新值應該等於該數與其下一層相鄰兩個數和的最小值 
		for(int i=n-2; i>=0; i--)
			for( int j=0; j<triangle[i].size(); j++)
				triangle[i][j] = min(triangle[i][j]+triangle[i+1][j],triangle[i][j]+triangle[i+1][j+1]);
		return triangle[0][0];
    }
};
int main()
{
	return 0;
}

LeetCode 64 最小路徑和

題目
給定一個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和爲最小。
說明:每次只能向下或者向右移動一步。
示例:
輸入:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
輸出: 7
解釋: 因爲路徑 1→3→1→1→1 的總和最小。

思路

從右至左,從底至上更新grid的每個值
grid的值代表的是該點移動到右下角點的最小路徑和

如果是最後一行,只能向右移動,grid[i][j] = grid[i][j]+grid[i][j+1]

如果是最後一列,只能向下移動,grid[i][j] = grid[i][j]+grid[i+1][j]

當可以向下走或者向右走,尋找最短路徑:grid[i][j] = min(grid[i][j]+grid[i][j+1],grid[i][j]+grid[i+1][j]);

代碼

//動態規劃解 
class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
    	//思路:從右至左,從底至上更新grid的每個值
		//grid的值代表的是該點移動到右下角點的最小路徑和
		int n = grid.size();
		if(n==0) return 0;
		int m = grid[0].size();
		
		for(int i=n-1; i>=0; i--) //從最後一行開始更新 
		{
			for(int j=m-1; j>=0;j--) //從最後一列開始更新
			{
				//判斷當前數是否可以向右向下走
				if(i==n-1 && j==m-1)
					continue;
				else if(i==n-1) //最後一行,只能向右走
					grid[i][j] += grid[i][j+1];
				else if(j==m-1) //最後一列,只能向下走
					grid[i][j] += grid[i+1][j];
				else //可以向下,也可以向右走,尋找最短路徑
					grid[i][j] = min(grid[i][j]+grid[i][j+1],grid[i][j]+grid[i+1][j]);
					 
			} 
		} 
		return grid[0][0];
    }
};

LeetCode 279 完全平方數

題目
給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, ...)使得它們的和等於 n。
你需要讓組成和的完全平方數的個數最少。

示例 1:
輸入: n = 12
輸出: 3 
解釋: 12 = 4 + 4 + 4.
示例 2:
輸入: n = 13
輸出: 2
解釋: 13 = 4 + 9.

思路

使用memo數組保存組成和爲i的完全平方數的最少個數。

想要計算每個memo[i],找到所有0,i之間的完全平方數x memo[i]=min(1+memo[i-x])

代碼

#include <iostream>
#include <vector>
using namespace std;

//動態規劃解 
class Solution {
public:
    int numSquares(int n) {
		vector<int> memo(n+1,0); //memo中存着組成n和的完全平方數的最少個數。
		memo[1] = 1; 
		//獲取每個memo[i] 
		for(int i=2; i<=n; i++)
		{
			memo[i] = 1+memo[i-1]; //第一種劃分方式是劃分成1和 i-1(i-1繼續劃分) 
			for(int j=1; i-j*j>=0 ; j++) //將i劃分爲 j*j  i-j*j 
			{
				if(i-j*j==0) //說明這個數是完全平方數 
				{
					memo[i]=1;
					continue;	
				}
				memo[i] =min(memo[i],1+ memo[i-j*j]); 
			}
		} 
		return memo[n];
    }
};
int main()
{
	return 0;
}

LeetCode 300 最長上升子序列

題目
給定一個無序的整數數組,找到其中最長上升子序列的長度。
示例:
輸入: [10,9,2,5,3,7,101,18]
輸出: 4 
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
說明:
可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。

思路

使用數組memo:memo[i]中存儲的是以nums[i]結尾的最長上升子序列長度

初始化:每個只有自身的上升子序列長度都爲1

對於當前的數字nums[i],遍歷nums[i]前面所有的數,找到一個比nums[i]小的數,說明nums[i]可以跟在找到的數後面 
memo[i]=找到所有可能nums[i]能跟在後面的值j memo[j]的最大值+1

代碼

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
		int n=nums.size();
		if(n==0) return 0;
		
		vector<int> memo(n,1);//memo[i]中存儲的是以nums[i]結尾的最長上升子序列長度
		
		for(int i=1; i<n; i++)
		{
			//遍歷nums[i]前面所有的數,找到一個比nums[i]小的數,nums[i]可以跟在找到的數後面 
			//找到所有可能結果的最大值 
			for(int j=0; j<i; j++) 
			{
				if(nums[i] > nums[j])
					memo[i] = max(memo[i],memo[j]+1); 
			}
		} 
		
		int res = 0;
		for(int i=0; i<n; i++)
			res = max(res,memo[i]);
		return res;
    }
};

LeetCode 322

給定不同面額的硬幣 coins 和一個總金額 amount。
編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。
如果沒有任何一種硬幣組合能組成總金額,返回-1。

示例1:
輸入: coins = [1, 2, 5], amount = 11
輸出: 3 
解釋: 11 = 5 + 5 + 1
示例 2:
輸入: coins = [2], amount = 3
輸出: -1

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
    	int Max = amount + 1;
		int n = coins.size();
		if(n==0) return -1;
		if(amount==0) return 0;
		//湊成i所需的少的硬幣個數。 
		vector<int> memo(amount+1,Max);
		memo[0] = 0;
		for(int i=0; i<=amount; i++)
		{
			//對於當前金額i memo[i]=min(memo[i-coins[j]+1) 
			for(int j=0; j<n; j++)
			{
				if(i>=coins[j])
				{
					memo[i] = min(memo[i],memo[i-coins[j]] + 1);
				}
			} 
		} 
		return memo[amount]>amount?-1:memo[amount];
    }
};

洛谷上的一道經典動態規劃問題 過河卒

思路

使用一個memo數組,存儲從座標(i,j)到達B點的路徑條數

如果一個點是馬所在的點或者馬的控制點,那麼該點無法到達B,memo[i][j]=0

對於每一個不是馬所在的點或者馬的控制點,從下至上,從右至左依次計算memo[i][j]:

如果點(i,j)是最後一行的點,那麼只能往右走,memo[i][j]=memo[i][j+1]

如果點(i,j)是最後一列的點,那麼只能往下走,memo[i][j]=memo[i+1][j]

其他點,可以向下或者向右走,memo[i][j] = memo[i+1][j]+memo[i][j+1];

代碼

bool inArea(int x, int y, int Bx, int By )
{
	return x>=0 && y>=0 && x<=Bx && y<=By;
}
int main()
{
	int Bx,By;
	int Mx,My;
	cin>>Bx>>By>>Mx>>My;
	vector<vector<long long> > memo(Bx+1,vector<long long>(By+1,-1));
	
	//先將馬所佔位置置爲0
	memo[Mx][My] = 0;
	for(int i= Mx-2; i<=Mx+2; i++)
	{
		if(i==Mx) continue;
		if(i==Mx-2 || i==Mx+2 )
		{
			if(inArea(i,My-1,Bx,By))
				memo[i][My-1] = 0;
			if(inArea(i,My+1,Bx,By))
				memo[i][My+1] = 0;
		}else
		{
			if(inArea(i,My-2,Bx,By))
				memo[i][My-2] = 0;
			if(inArea(i,My+2,Bx,By))
				memo[i][My+2] = 0;
		}
	} 
	//先算出最後一行的全部解
	memo[Bx][By] = 1;
	for(int i=By-1; i>=0; i--)
	{
		if(memo[Bx][i]!=0) 
			memo[Bx][i] = memo[Bx][i+1];
	}
	//遍歷每一行 
	for(int i=Bx-1; i>=0; i--)
	{
		for(int j=By; j>=0; j--)
		{
			if(memo[i][j]!=0)
			{
				if(j==By)
				{
					memo[i][j] = memo[i+1][j];
				}else
				{
					memo[i][j] = memo[i+1][j]+memo[i][j+1];
				}
			}
		}
	} 	
	cout<<memo[0][0]<<endl;	
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章