动态规划——划分型
動態規劃——劃分型
目錄
1. 劃分型動態規劃概述
給定長度為N的序列或字符串,要求劃分成若干段
2. 最少將n分成幾個完全平方數之和
1. 題目描述:給定一個正整數n,問最少可以將n分成幾個完全平方數之和
例子:
輸入:n=13
輸出:2(13=4+9)
2. 思路
確定狀態
轉移方程
f[i] = min(1<=jj<=i){f[i-jj] + 1}
初始條件和邊界情況
初始條件:f[0] = 0
計算順序
初始化 f[0]
計算f[1]…f[N]
答案是 f[N]
3. 代碼實現
public static int numSquares(int n) {int[] f = new int[n];f[0] = 0;for (int i = 1; i <= n; i++) {f[i] = Integer.MAX_VALUE;for (int j = 1; j * j <= i; j++) {f[i] = Math.min(f[i - j * j] + 1, f[i]);}}return f[n];}3. 求字符串劃分回文串的最少次數
1. 題目描述
給定一個字符串S[0…N-1],將這個字符串劃分成若干段,每一段都是一個回文串,求最少劃分次數
例如:
輸入:“aab”
輸出:1(劃分1次->“aa”,“b”)
2. 思路
確定狀態
關注最優策略中最后一段回文串,設為S[j…N-1],那么則需要知道S前 j 個字符[0…j-1]最少可以劃分成幾個回文串
轉移方程
設 f[i] 為前 i 個字符 S[0…i-1] 最少可以劃分成幾個回文串
f[i] = min(j=0,…i-1){f[j]+1|S[j…i+1]是回文串}
初始條件和邊界情況
初始條件:f[0] = 0
計算順序
f[0],f[1],…f[N]
答案是 f[N]-1
3. 代碼實現
private boolean[][] calcPalin(char[] s) {int n = s.length;boolean[][] f = new boolean[n][n];int i, j, c;for (i = 0; i < n; i++) {for (j = i; j < n; j++) {f[i][j] = false;}}//奇數for (c = 0; c < n; c++) {i = j = c;while (i >= 0 && j < n && s[i] == s[j]) {f[i][j] = true;i--;j++;}}//偶數for (c = 0; c < n; c++) {i = c;j = c + 1;while (i >= 0 && j < n && s[i] == s[j]) {f[i][j] = true;i--;j++;}}return f;}public int minCut(String ss) {char[] s = ss.toCharArray();int n = s.length;if (n == 0) {return 0;}boolean[][] isPalin = calcPalin(s);int[] f = new int[n + 1];f[0] = 0;for (int i = 1; i <= n; i++) {f[i] = Integer.MAX_VALUE;for (int j = 0; j < i; j++) {if (isPalin[j][i - 1]) {f[i] = Math.min(f[i], f[j] + 1);}}}return f[n] - 1;}4. 求最短時間抄寫完所有的書
1. 題目描述
有N本書需要被抄寫,第i本書有A[i]頁,i=0,1,…N-1。
有K個抄寫員,每個抄寫員可以抄寫連續的若干本書(例如:第3~5本書,或者第10本書),每個抄寫員的抄寫速度都一樣:一分鐘一頁。
最少需要多少時間抄寫完所有的書
例子:
輸入:A=[3,2,4],K=2
輸出:5(第一個抄寫員抄寫第1本和第2本,第二個抄寫員抄寫第3本書)
2. 思路
最后一步:最優策略中最后一個抄寫員Bob(設他為第K個)抄寫的部分為一段連續的書,包含最后一本,如果Bob抄寫第j本到n-1本書,則Bob需要時間A[j]+…+A[N-1]
則需要知道前面K-1個人最少需要多少時間抄完前j本書(第0~i-1本書)
狀態:設f[k][i]為前k個抄寫員最少需要多少時間抄完前i本書
設f[k][i]為前k個抄寫員最少需要多少時間抄完前i本書
f[k][i] = min(j=0,…i){max{f[k-1][j], A[j]+…+A[N-1]}}
0個抄寫員只能抄0本書:f[0][0] = 0,f[0][1] = f[0][2] = … = f[0][N] = 正無窮
k個抄寫員(k>0)需要0時間抄0本書:f[k][0] = 0 (k>0)
如果K>N,可以賦值K=N
計算 f[0][0],f[0][1],…f[0][N]
計算 f[1][0],f[1][1],…f[1][N]
…
計算 f[K][0],f[K][1],…f[K][N]
時間復雜度O(N^2K),空間復雜度O(NK)
3. 代碼實現
public int copyBook(int K, int[] A) {int n = A.length;if (n == 0) {return 0;}int[][] f = new int[K + 1][n + 1];f[0][0] = 0;for (int i = 1; i <= n; i++) {f[0][i] = Integer.MAX_VALUE;}for (int k = 1; k <= K; k++) {f[k][0] = 0;for (int i = 1; i <= n; i++) {f[k][i] = Integer.MAX_VALUE;int sum = 0;for (int j = i; j >= 0; j--) {f[k][i] = Math.min(f[k][i], Math.max(f[k - 1][j], sum));if (j > 0) {sum += A[j - 1];}}}}return f[K][n];}5. 解密方式
1. 題目描述
例子:
輸入:12
輸出:2(AB或者L)
2. 思路
1. 解密數字串即劃分成若干段數字,每段數字對應一個字母
2. 最后一步:對應一個字母A, B,…,Z,這個字母加密時變成1, 2, …26
一共100+50=150種解密方式
3. 要求數字串前N個字符的解密方式數,需要知道數字串前N-1和N-2個字符的解密方式數
1. 狀態:設數字串S前i個數字解密成字母串有f[i]種方式
初始條件:f[0]=1,即空串有1種方式解密
邊界情況:如果i=1,只看最后一個數字
f[0], f[1],…,f[N]
答案是f[N]
3. 代碼實現
public int numDecodings(String ss) {if (ss.length() == 0) {return 1;}char[] s = ss.toCharArray();int n = s.length;int[] f = new int[n + 1];f[0] = 1;for (int i = 1; i <= n; i++) {f[i] = 0;if (s[i - 1] >= '1' && s[i - 1] <= '9') {f[i] += f[i - 1];}if (i > 1) {int num = 10 * (s[i - 2] - '0') + (s[i - 1] - '0');if (num >= 10 || num <= 26) {f[i] += f[i - 2];}}}return f[n];}總結