leetcode中的DP題目總結
[leetcode 413]Arithmetic Slices
dp解釋
注意
- dp中經常會設計有以i結尾的**的個數
程式
public int numberOfArithmeticSlices(int[] A) {
int len = A.length;
if(len < 3 ) return 0;
int result = 0;
int[] dp = new int[len];
for(int i = 1; i < len-1; i++) {
if(A[i+1] - A[i] == A[i] - A[i-1]) {
dp[i+1] = dp[i] + 1;
result += dp[i+1];
} else {
dp[i+1] = 0;
}
}
return result;
}
[leetcode 375] Guess Number Higher or Lower II
題目
給一個範圍n,對方心裡想了某個數字(範圍在[1,n],具體大小你並不知道)。你每次猜一個數字,對方會告訴你猜得大了還是猜的小了或者猜對了。如果這次沒有猜對,你需要支付你猜的這個數字的大小。問,給定一個範圍n時,最少支付多少才能保證一定猜到這個數字呢?
解釋
這個問題有點繞,然而卻是一個典型的minmax問題。可以先從簡單的例子來想。
在一個範圍[m,n]中猜數字,
1. 如果只有一個數,比如[5,5],那麼一次就會猜中,就不用支付,因此最少支付0。
2. 如果範圍裡有兩個數,比如[5,6],你猜5如果錯了,你就知道是6了,需要支付5;你猜6如果錯了,同樣一定會知道答案,但是需要支付6。相比之下,支付5就可以一定猜出來。
3. 如果範圍裡有3個數,比如[5,6,7],你猜5,那麼[6,7]中猜6一定知道答案,共支付11;你猜6,[5,5]和[7,7]不用支付就可以知道答案,共支付6;你猜7,那麼[5,6]需要支付5,共支付12;比較11,6,12可知,最少支付為6。
4. 一般的情況,我們不管範圍裡有幾個數,用
注意
大範圍需要先知道小範圍,用什麼順序來求dp陣列呢?如下圖:
程式
public int getMoneyAmount(int n) {
int dp[][] = new int[n+1][n+1];
for(int j = 1; j <= n; j++) {
for(int i = 1; i + j <= n; i++) {
dp[i][i+j] = Integer.MAX_VALUE;
for(int k = i; k <= i+j; k++) {
dp[i][i+j] = Math.min(dp[i][i+j], k + Math.max(dp[i][k-1], (k+1 > i+j)?0:dp[k+1][i+j]));
}
}
}
return dp[1][n];
}
[leetcode 115] Distinct Subsequences
題目
給定兩個字串S,T。S中有多少個不同的子序列恰好等於T
這個題目很容易理解錯,題目想問的並不是S和T有多少個不同的公共子序列。舉一個例子
S=”rabbbit”, T=”rabbit”,則返回3。原因是:
S的三個子序列rab-bit , ra-bbit,rabb-it恰好等於T,因此返回3。
DP解釋
用
當
當
以上的遞推式的意思是:
1. S增加一個字母,但是和T的當前字母不相等時,S不會增加新的子序列和T相等,因此
2. S增加一個字母,和T的當前字母相等時,S會增加
注意
- 計算的順序是一行一行算
- 初值的設計是dp[0][any]均為1,可以理解為S的空序列等於一個空串,因此為1
- 只需要計算j>=i的情況,因為若S比T短,那一定dp一定為0
- 思考dp遞推式時,定住T的長度,增加S的長度來思考會更容易想明白問題
程式
public int numDistinct(String s, String t) {
int lens = s.length();
int lent = t.length();
int[][] dp = new int[lent+1][lens+1];
for(int i = 0; i <= lens; i++)
dp[0][i] = 1;
for(int j = 1; j <= lent; j++) {
for(int i = j; i <= lens; i++) {
if(s.charAt(i-1) == t.charAt(j-1))
dp[j][i] = dp[j-1][i-1]+dp[j][i-1];
else
dp[j][i] = dp[j][i-1];
}
}
return dp[lent][lens];
}
[leetcode 120] Triangle
題目
一個三角形從頂向下尋找一條和最小的路徑
DP解釋
- 一個最普通的想法:自頂向下,
dp[i][j] 為走到第i 層的第j 個節點是的和最小路徑的和,那麼則有dp[i][j]=min(dp[i−1][j−1],dp[i−1][j]) ,由於每層的資訊沒有必要都存下來,因此只用存當前層,則空間複雜度為O(n) 。自頂向下的缺點是:需要特殊處理邊界情況,並且還需要求最底層的最小值。 - 更簡潔的方法:自底向上,考慮到從上向下走的最小值和從下向上走是沒有區別的。由於到第i層第j個節點只能走向第i+1層的第j個節點和第j+1個節點,因此從向上走到第i層第j個節點的最小路徑的總和表示為
dp[i][j] ,則有dp[i][j]=min(dp[i+1][j],dp[i+1]