1. 程式人生 > >動態規劃類整理

動態規劃類整理

1.最大子序和
給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。

動態規劃,狀態轉移方程:dp[i] = max{A[i], dp[i-1]+A[i]}

	public static int maxSubArray(int[] nums) {
		if (nums == null)
			return 0;
		int len = nums.length;
		int max = nums[0];
		int[] dp = new int[len];
		dp[0] = nums[0];
		for (int i = 1; i < len; i++) {
			if (dp[i - 1] > 0)
				dp[i] = dp[i - 1] + nums[i];
			else
				dp[i] = nums[i];
		}

		for (int i = 0; i < len; i++) {
			if (max < dp[i])
				max = dp[i];
		}
		return max;
	}

不借助dp陣列:
一定先判斷 if(max<sum)後 if(sum<0);
因為如果全負數的情況,sum=0;會影響導致結果為0;

    public int maxSubArray(int[] nums) {
        if (nums == null)
			return 0;
		int len = nums.length;
		int max = nums[0];
		int sum = 0;
		for(int i=0;i<len;i++) {
			sum=sum+nums[i];
			if(max<sum)
				max=sum;
			 if(sum<0)
				sum=0;
		}
		return max;
    }

2.爬樓梯
假設你正在爬樓梯。需要 n 階你才能到達樓頂。

每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?

注意:給定 n 是一個正整數。

示例 1:

輸入: 2
輸出: 2
解釋: 有兩種方法可以爬到樓頂。
1 階 + 1 階
2 階
思路:跳臺階問題

    public int climbStairs(int n) {
        if (n <= 0)
			return 0;
		if (n < 3)
			return n;
		int f1 = 1, f2 = 2, res = 0;
		for (int i = 3; i <= n; i++) {
			res = f1 + f2;
			f1 = f2;
			f2 = res;
		}
		return res;
    }

300.最長上升子序列
給定一個無序的整數陣列,找到其中最長上升子序列的長度。

示例:

輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
思路:動態規劃dp[i] 儲存的是以i為最後元素的,最長子序列長度;
因此需要內迴圈j從0遍歷到i,如果存在比nums[i]小的元素,則計算dp轉移方程;
dp[i]= max(dp[i], dp[j] + 1);

注意:此類題是O(N2),上升子序列不是連續的;

    public int lengthOfLIS(int[] nums) {
     	if(nums==null)
			return 0;
		int len = nums.length;
		int[] dp=new int[len];
		for(int i=0;i<len;i++){
			dp[i]=1;
		}
		int res = 0;
		for(int i=0;i<len;i++) {
			for(int j=0;j<i;j++) {
				if(nums[i]>nums[j])
					dp[i]=dp[i]>dp[j]+1?dp[i]:dp[j]+1;
			}
			res=res>dp[i]?res:dp[i];
		}
		return res;
    }

674.最長連續遞增序列
給定一個未經排序的整數陣列,找到最長且連續的的遞增序列。

示例 1:

輸入: [1,3,5,4,7]
輸出: 3
解釋: 最長連續遞增序列是 [1,3,5], 長度為3。
儘管 [1,3,5,7] 也是升序的子序列, 但它不是連續的,因為5和7在原數組裡被4隔開。
思路:設計數器count,如果遇到更大的數,則加一;否則,計數器歸一;
res始終儲存當前最大count值;

	public static int findLengthOfLCIS(int[] nums) {
		if(nums==null)
			return 0;
		int res=1;
		int len = nums.length;
		int cur=nums[0];
		int count=1;
		for(int i=1;i<len;i++) {
			if(nums[i]>cur)
				count++;
			else
				count=1;
			res=res>count?res:count;
			cur=nums[i];
		}
		return res;
	}

對於兩個字串,請設計一個高效演算法,求他們的最長公共子序列的長度,這裡的最長公共子序列定義為有兩個序列U1,U2,U3…Un和V1,V2,V3…Vn,其中Ui&ltUi+1,Vi&ltVi+1。且A[Ui] == B[Vi]。

給定兩個字串A和B,同時給定兩個串的長度n和m,請返回最長公共子序列的長度。保證兩串長度均小於等於300。

測試樣例:
“1A2C3D4B56”,10,“B1D23CA45B6A”,12
返回:6
思路:
動態規劃;dp[i][j]表示A字串的前i字元與B串的前j字元的最長公共子序列的長度;
轉移方程:當A[i]==B[j],dp[i][j]=dp[i-1][j-1]+1;
否則dp[i][j]=max{dp[i-1][j],dp[i][j-1]};
注意:子序列問題同子串問題區分;子串是連續的;

	public static int findLCS(String A, int n, String B, int m) {
		int[][] dp = new int[n + 1][m + 1];
		dp[0][0] = 1;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				if (A.charAt(i - 1) == B.charAt(j - 1))
					dp[i][j] = dp[i - 1][j - 1] + 1;
				else
					dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
			}
		}
		return dp[n][m];
	}

最長公共連續子串
輸入
abcde
abgde
輸出
2
思路:與上題類似,動態規劃思想;
主要區別是,dp矩陣意義為A串以i結尾與B串以j結尾的最長公共連續子串;
當A[i-1] = B[j-1]時,dp[i][j] = dp[i -1][j -1] + 1;
A[i-1] != B[j-1]時,以他們結尾的公共子串長度必然為0;
最後從dp中取出長度最大的值即為最長公共子串;

	 public static int findLongest(String A, int n, String B, int m) {
		 int [][]dp=new int[n+1][m+1];
		 int max=0;
		 for(int i=1;i<=n;i++) {
			 for(int j=1;j<=m;j++) {
				 if (A.charAt(i - 1) == B.charAt(j - 1))
						dp[i][j] = dp[i - 1][j - 1] + 1;
				 else
					 dp[i][j]=0;
				 max=Math.max(max, dp[i][j]);
			 }
		 }
		 return max;
	 }