1. 題目
轉自:原文
所謂回文數就是一個數字,從左邊讀和從右邊讀的結果都是一樣的,例如12321。
現在有一個只包含1,2,3的數字,你可以通過在任意位置增加一位數字或者刪除一位數字來將其變成一個回文數。但是增加或刪除不同數字所需要的代價是不一樣的。
已知增加和刪除每個數字的代價如下:
* 增加一個 1,代價:100;刪除一個 1,代價:120。
* 增加一個 2,代價:200;刪除一個 2,代價:350。
* 增加一個 3,代價:360;刪除一個 3,代價:200。
* 增加一個 4,代價:220;刪除一個 4,代價:320。
請問如何通過最少的代價將一個數字變換為一個回文數。當然,如果一個數字本身已經是一個回文數(包括一位數,例如:2),那么變換的代價為 0。
輸入描述:
單組輸入。輸入一個由1、2、3、4組成的正整數,正整數位數<=100位。【提示:采用字符串輸入】
輸出描述:
輸出一個整數,表示將輸入數字變換為一個回文數所需的最少代價。
2. 樣例
輸入:12322
輸出:300
提示:
增加一個 1 並增加一個 2,將輸入正整數變為 1223221 或者 2123212,所需代價最小,為:100+200=300。
3. 題解
解答這道題后,原來的題目就很好解決了,首先回文串就是從左到右和從右到左遍歷是完全一樣的結果,這道題有兩個思路,都是動態規划,找到狀態轉移方程求解,第一個是直接對原字符串分析,另一個是將字符串進行反轉,求兩個串的最長子序列的長度,然后總長度減去最長子序列的長度就是最少操作次數,有興趣可以試一下這個思路。本文主要是說第一種方法,直接分析原字符串,找出狀態轉移方程,
首先,dp[i][j]表示字符串位置 i 到 j 為回文串的最少操作次數
當s[i]==s[j]時,dp[i][j]=dp[i+1][j-1] ,因為i位置和j位置上的字符相同,所以位置i到j的字符串變換操作次數取決於i到j中間的字符串變換為回文串的最少操作次數。
否則,說明i位置j位置上的字符不相同,那么i到j位置的字符串變換成回文串的操作次數取決去從i到j-1位置的操作次數和從i+1到j位置的操作次數,哪個小選擇變換哪個。SO:
dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;
分析到此結束,看代碼:(注意兩層循環的設定,循環設置方式不唯一)
class Solution {
public:
int minInsertions(string s) {
int n = s.size();
vector<vector<int>>dp(n, vector<int>(n, 0));
for (int i = n - 2; i >= 0; i--)
{
for (int j = i + 1; j < n; j++)
{
if (s[i] == s[j])
dp[i][j] = dp[i + 1][j - 1];
else
{
dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;
}
}
}
return dp[0][n-1];
}
};
經過上面對於字符串變換的最小操作次數了解后,再回看這道變換為回文數的最小代價題。
首先根據題意,增加和刪除的操作代價不相同,因為我們可以自己初始化一個最小代價數組,對於同一個數字,增加和刪除的代價我們選擇較小的那個。
所以可以定義一個數組arr[5]={0,100,200,200,220},數組的下標代表操作的數字,對應的值就代表操作該數字的代價,比如下標arr[1]=100表示對原串增加一個數字1,代價為100。(因為如果需要操作數字1,增加或刪除肯定是選擇代價更小的進行)
基於之前的分析,狀態轉移方程是一個道理,當s[i]==s[j]時,dp[i][j]=dp[i+1][j-1]
否則,我們不再是直接計算 dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;
因為上述不關心你操作的數據是多少,獲得min(dp[i+1][j],dp[i][j-1])后需要操作字符s[i]還是s[j]不關心,因為不管操作哪一個都只是一次操作,但是本題需要比較不同的選擇付出的代價。
我們有兩個選擇。選擇操作s[i],dp[i][j]=arr[s[i]-'0']+dp[i+1][j] 即操作s[i]的代價加上i+1到j區間的字符串的代價;選擇操作s[j],dp[i][j]=arr[s[j]-'0']+dp[i][j-1] 即操作s[j]的代價加上i到區間j+1的字符串的代價;我們選取兩個選擇中代價更小的一個作為dp[i][j]的值。
結合代碼,如下:
1 public class n3 {
2 private static int[] add = {0, 100, 200, 360,220};
3 private static int[] dec = {0, 120, 350, 200, 320};
4 public static void main(String[] args) {
5 Scanner scanner = new Scanner(System.in);
6 String s = scanner.nextLine();
7 int n = s.length();
8 int[][] dp = new int[n][n];
9 for(int i = n - 1; i >=0; i--) {
10 for(int j = i + 1; j < n; j++) {
11 // 相等,只需要比較之間的數值
12 if(s.charAt(i) == s.charAt(j)) {
13 dp[i][j] = dp[i + 1][j - 1];
14 } else {
15 // 操作i
16 int dp1 = dp[i + 1][j] + Math.min(add[s.charAt(i) - '0'], dec[s.charAt(i) - '0']);
17 int dp2 = dp[i][j - 1] + Math.min(add[s.charAt(j) - '0'], dec[s.charAt(j) - '0']);
18 dp[i][j] = Math.min(dp1, dp2);
19 }
20 }
21 }
22 System.out.println(dp[0][n - 1]);
23 }
24 }
4. 結語
努力去愛周圍的每一個人,付出,不一定有收獲,但是不付出就一定沒有收獲! 給街頭賣藝的人零錢,不和深夜還在擺攤的小販討價還價。願我的博客對你有所幫助(*^▽^*)(*^▽^*)!
如果客官喜歡小生的園子,記得關注小生喲,小生會持續更新(#^.^#)(#^.^#)。