日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态顺序字符串基本操作实验_掌握套路,你也会用动态规划

發布時間:2025/3/20 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态顺序字符串基本操作实验_掌握套路,你也会用动态规划 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

介紹

動態規劃并不是一種具體的算法,而是一種思想,個人覺得就是緩存+枚舉,把求解的問題分成許多階段或者多個子問題,然后按順序求解各子問題。前一子問題的解為后一子問題提供了有用的信息。在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟棄其他局部解。依次解決各子問題,最后一個子問題就是初始問題的解。

所以動態規劃一般用來求最優解(對子問題進行決策),求種類數(對子問題進行加和)

先分享幾個經典的動態規劃實現,后續再分析幾個面試題

最長上升子序列

來源:LeetCode 300.最長上升子序列

描述:給定一個無序的整數數組,找到其中最長上升子序列的長度。

示例:

輸入: [10,9,2,5,3,7,101,18] 輸出: 4 解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。

可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。你算法的時間復雜度應該為 O(n2) 。進階: 你能將算法的時間復雜度降低到 O(n log n) 嗎?

思路:子序列有很多,最長的長度為4

我們假設dp[i]存的是到第i個元素時,數組的最長子序列,則對應的狀態轉移方程為

dp[i] = max{1, dp[j] + 1 | j < i 且 arr[j] < arr[i]}

其中1為只有自己一個元素,則遞增子序列的長度為1

public class Solution {public int lengthOfLIS(int[] nums) {int max = 0;int[] dp = new int[nums.length];for (int i = 0; i < nums.length; i++) {dp[i] = 1;for (int j = 0; j < i; j++) {if (nums[i] > nums[j] && (dp[j] + 1) > dp[i]) {dp[i] = dp[j] + 1;}}if (dp[i] > max) {max = dp[i];}}return max;} }

數塔問題

來源:LeetCode 120. 三角形最小路徑和描述:給定一個三角形,找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。

相鄰的結點 在這里指的是 下標 與 上一層結點下標 相同或者等于 上一層結點下標 + 1 的兩個結點。

例如,給定三角形:

[[2],[3,4],[6,5,7],[4,1,8,3] ]

自頂向下的最小路徑和為 11(即,2 + 3 + 5 + 1 = 11) 說明:如果你可以只使用 O(n) 的額外空間(n 為三角形的總行數)來解決這個問題,那么你的算法會很加分。

思路:把這個圖形換一下,方便講遞推公式

[2], [3,4], [6,5,7], [4,1,8,3]

我們可以從底到頂來算最優值。dp[i][j]為從最底部到第i行第j列的最小路徑和,value[i][j]為第i行第j列的值,狀態轉移方程為

dp[i][j] = max(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]

——————————————————————————————————————

public class Solution {public int minimumTotal(List<List<Integer>> triangle) {if (triangle == null || triangle.size() == 0) {return 0;}// 這里行和列加1,是為了不用處理最下面一行的邊界int[][] dp = new int[triangle.size() + 1][triangle.size() + 1];for (int i = triangle.size() - 1; i >= 0; i--) {List<Integer> rows = triangle.get(i);for (int j = 0; j < rows.size(); j++) {dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + rows.get(j);}}return dp[0][0];} }

最長公共子串

來源:LeetCode 1143. 最長公共子序列描述:給定兩個字符串 text1 和 text2,返回這兩個字符串的最長公共子序列的長度。

一個字符串的 子序列 是指這樣一個新的字符串:它是由原字符串在不改變字符的相對順序的情況下刪除某些字符(也可以不刪除任何字符)后組成的新字符串。例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。兩個字符串的「公共子序列」是這兩個字符串所共同擁有的子序列。

若這兩個字符串沒有公共子序列,則返回 0。示例 1:

輸入:text1 = "abcde", text2 = "ace" 輸出:3 解釋:最長公共子序列是 "ace",它的長度為 3。

示例 2:

輸入:text1 = "abc", text2 = "abc" 輸出:3 解釋:最長公共子序列是 "abc",它的長度為 3。

示例 3:

輸入:text1 = "abc", text2 = "def" 輸出:0 解釋:兩個字符串沒有公共子序列,返回 0。

思路:這個題確實比較抽象,上圖

s1=a s2=a,最長公共子串長度為1 s1=ac s2=abc,對應的公共子串長度為2

dp[i][j]為第一個字符串長度為i和第二個字符串長度為j時對應的最長公共子串 狀態轉移方程為

if(s1.charAt(i) == s2.charAr(j))dp[i][j] = dp[i-1][j-1] + 1; elsedp[i][j] = Math.max(dp[i-1][j], dp[i][j -1]);

還是畫圖演示一下遞推公式

public class Solution {public int longestCommonSubsequence(String text1, String text2) {if (text1 == null || text2 == null || text1.length() == 0 || text2.length() == 0) {return 0;}int[][] dp = new int[text1.length() + 1][text2.length() + 1];for (int i = 1; i <= text1.length(); i++) {for (int j = 1; j <= text2.length(); j++) {if (text1.charAt(i - 1) == text2.charAt(j - 1))dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);}}return dp[text1.length()][text2.length()];} }

背包問題

是男人就看《背包九講》,作為動態規劃的入門課,《背包九講》必不可少。這次就只分享背包九講中最簡單的01背包

來源:藍橋杯

問題描述:給定N個物品,每個物品有一個重量W和一個價值V.你有一個能裝M重量的背包.問怎么裝使得所裝價值最大.每個物品只有一個. 輸入格式   輸入的第一行包含兩個整數n, m,分別表示物品的個數和背包能裝重量。  以后N行每行兩個數Wi和Vi,表示物品的重量和價值 輸出格式   輸出1行,包含一個整數,表示最大價值。樣例輸入 3 5 2 3 3 5 4 7 樣例輸出 8 數據規模和約定 1<=N<=200,M<=5000.

思路:這是最簡單的01背包,都不帶變形的,每種物品僅有一件,可以選擇放或不放。

第i件物品的重量是w[i],價值是v[i] 用dp[i][j]表示前i件物品放入一個承重為j的背包可以獲得的最大價值,狀態轉移方程為

dp[i][j] = max{dp[i-1][j], dp[i-1][j-w[i]] + c[i]}

dp[i][j] = max{不放第i件物品,放第i件物品}

你可以照著這個狀態轉移方程自己寫一下,我下面這種寫法直接用了滾動數組,把數組從二維變成了一維,節省了空間,有興趣的可以參考其他博客學習這種寫法,本文就不深入了。

public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int n = in.nextInt();int m = in.nextInt();int[] widght = new int[210];int[] value = new int[210];int[] dp = new int[5010];for (int i = 0; i < n; ++i) {widght[i] = in.nextInt();value[i] = in.nextInt();}for (int i = 0; i < n; ++i) {for (int j = m; j >= widght[i]; j--) {dp[j] = Math.max(dp[j], dp[j - widght[i]] + value[i]);}}System.out.println(dp[m]);} }

不同路徑

來源:LeetCode 62不同路徑

描述:一個機器人位于一個 m x n 網格的左上角 (起始點在下圖中標記為“Start” )。機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記為“Finish”)。問總共有多少條不同的路徑?

例如,上圖是一個7 x 3 的網格。有多少可能的路徑?說明:m 和 n 的值均不超過 100。

示例:輸入: m = 3, n = 2,輸出: 3 解釋:從左上角開始,總共有 3 條路徑可以到達右下角。

  • 向右 -> 向右 -> 向下
  • 向右 -> 向下 -> 向右
  • 向下 -> 向右 -> 向右
  • 思路:這個大家一下就會想到用遞歸解決,假設f(m,n)表示移動到點(m,n)的路徑數,因為機器人智能向下或者向右移動,所以點(m,n)只能從點(m-1,n)和(m,n-1)移動而來,遞歸公式就是f(m,n)=f(m-1,n)+f(m,n-1),遞歸的出口呢?當然就是網格的邊界了,網格邊界上的點都只有一種方法,按照這種思路寫出來如下代碼

    class Solution {public int uniquePaths(int m, int n) {// 在網格邊界的格子只能有一種走法if (m == 1 || n == 1) {return 1;}// m,n這個位置只能從(m - 1 , n)和(m, n - 1)移動而來return uniquePaths(m - 1, n) + uniquePaths(m, n - 1);} }

    其實這個代碼效率還是很低的,因為有很多重復的計算,如下圖

    當m和n為(3,3)時,(2,2)被計算了2次,而且m和n越大,重復計算的次數最多,我們可以把已經算出來的值保存一下,這樣下次再用的時候就不用算了,直接取就行,叫做備忘錄算法,grid[m][n]表示走到(m,n)這個點時的路徑數。

    class Solution {public static int[][] grid = new int[110][110];public int uniquePaths(int m, int n) {if (grid[m][n] != 0)return grid[m][n];if (m == 1 || n == 1) {return 1;}return grid[m][n] = uniquePaths(m - 1, n) + uniquePaths(m, n - 1);} }

    當值不為0的時候說明已經被算過了,直接取就行了,否則就得計算并保存結果,這樣效率提高了不少,但是如果m和n特別大,遞歸層數過多時會造成堆棧溢出的,該怎么辦?這個時候就得用到動態規劃了

    遞歸是從上至下開始計算的,有沒有可能從下而上的計算呢?,如先算出(1,2)和(2,1),然后就能算出(2,2)了,我們得按照一定的規律計算,保證在算(2,2)之前,(1,2)和(2,1)已經算完了,我們只要按行從左到右計算,或者按列從上到下即可

    dp[i][j]表示到達第i行第j列的路徑數,所以狀態轉移方程為

    dp[i][j] = dp[i][j-1] + dp[i-1][j]

    ——————————————————————————————————————

    class Solution {public static int[][] grid = new int[110][110];public int uniquePaths(int m, int n) {for (int i = 1; i <= n ; i++) {for (int j = 1; j <= m ; j++) {if (i == 1 || j == 1)grid[i][j] = 1;elsegrid[i][j] = grid[i][j-1] + grid[i-1][j];}}return grid[n][m];} }

    減繩子

    來源:《劍指offer》第二版

    描述:給你一根長度為n的繩子,請把繩子剪成m段 (m和n都是整數,n>1并且m>1)每段繩子的長度記為k[0],k[1],…,k[m].請問k[0]k[1]…*k[m]可能的最大乘積是多少?例如,當繩子的長度為8時,我們把它剪成長度分別為2,3,3的三段,此時得到的最大乘積是18.

    思路:定義函數f(n)為長度為n的繩子剪成若干段后各段長度乘積的最大值。在剪第一刀的時候,我們有n-1種可能的選擇,也就是剪出來的第一段繩子的長度分別為1,2...n-1。因此f(n)=max(f(i)*f(n-i)),其中0<i<n。這是一個從上至下的遞歸公式,遞歸會有很多重復的子問題。我們可以從下而上的順序計算,也就是說我們先得到f(2),f(3),再得到f(4),f(5),直到得到f(n)

    假設dp[i]表示長度為i的繩子能得到的最大乘積,則狀態轉移方程為

    dp[i] = max(dp[i], dp[j] * dp[i-j])

    ——————————————————————————————————————

    public class Solution {public int maxNumAfterCutting(int n) {if (n < 2)return 0;// 繩子長度為2時,只能剪成1和1if (n == 2)return 1;// 只可能為長度為1和2的2段或者長度都為1的三段,最大值為2if (n == 3)return 2;// 當長度大于3時,長度為3的段的最大值是3int product[] = new int[n+1];product[0] = 0;product[1] = 1;product[2] = 2;product[3] = 3;int max = 0;for (int i = 4; i <= n; i++) {max = 0;for (int j = 1; j <= i / 2; j++) {int sum = product[j] * product[i - j];if (sum > max) {max = sum;product[i] = max;}}}return product[n];}}

    代碼中第一個for循環變量i是順序遞增的,這意味著計算順序是自下而上的。因此再求f(i)之前,對于每一個j(0<i<j)而言,f(j)都已經求解出來了,并且保存在product[j]里。為了求解f(i),我們需要求出所有可能的f(i)*f(i-j)并比較得出他們的最大值,這就是代碼中第二個for循環的功能

    這個面試題又比第一個面試題難了一點,因為第一個面試題僅僅是將一個大問題劃分成幾個子問題,并沒有根據局部解進行決策得到最優解,而這個面試題體現了決策的過程

    接雨水

    來源:LeetCode 42. 接雨水

    描述:給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖,計算按此排列的柱子,下雨之后能接多少雨水

    上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 個單位的雨水(藍色部分表示雨水)

    示例:輸入: [0,1,0,2,1,0,1,3,2,1,2,1]輸出: 6

    思路:思路還是比較簡單,對每一個柱子能存多少水求和即可,這樣只需要獲取這個柱子左邊的最高高度和這個柱子右邊的最高高度,2者的最小值減去柱子的高度就是這個柱子的存水量

    class Solution {public int trap(int[] height) {int sum = 0;for (int i = 0; i < height.length; i++) {int maxLeft = 0, maxRight = 0;// 對i這個柱子,左邊柱子的最高值for (int left = 0; left < i; left++) {maxLeft = Math.max(maxLeft, height[left]);}// 對i這個柱子,右邊柱子的最高值for (int right = i + 1; right < height.length ; right++) {maxRight = Math.max(maxRight, height[right]);}// i這個柱子能存的水量int temp = Math.min(maxLeft, maxRight) - height[i];if (temp > 0)sum += temp;}return sum;} }

    每次都要算某個柱子的左右最值,時間復雜度是O(n2),能不能把算左右最值的效率提高呢?這就用到動態規劃了,假如說

    我們用dp[i],表示到第i個柱子(包括第i個柱子)左邊的最大值,height[i]為第i個柱子的高度,右邊同理,則狀態轉移方程為

    dp[i] = max(dp[i-1], height[i])

    ——————————————————————————————————————

    class Solution {public int trap(int[] height) {int sum = 0;int len = height.length;if (len == 0)return 0;int[] maxLeft = new int[len];int[] maxRight = new int[len];maxLeft[0] = height[0];for (int i = 1; i < len; i++) {maxLeft[i] = Math.max(height[i], maxLeft[i-1]);}maxRight[len - 1] = height[len - 1];for (int i = len - 2; i >= 0; i--) {maxRight[i] = Math.max(height[i] ,maxRight[i+1]);}for (int i = 0; i < height.length; i++) {sum += Math.min(maxLeft[i], maxRight[i]) - height[i];}return sum;} }

    這樣時間復雜度就變成O(n)了

    分割等和子集

    來源:LeetCode 416. 分割等和子集

    描述:給定一個只包含正整數的非空數組。是否可以將這個數組分割成兩個子集,使得兩個子集的元素和相等。

    注意:

    每個數組中的元素不會超過 100 數組的大小不會超過 200 示例 1:

    輸入: [1, 5, 11, 5] 輸出: true 解釋: 數組可以分割成 [1, 5, 5] 和 [11].

    示例 2:

    輸入: [1, 2, 3, 5] 輸出: false 解釋: 數組不能分割成兩個元素和相等的子集.

    思路:很典型的01背包,背包的容量為元素和的一半,最后看背包是否能填滿即可 之前已經說了01背包的狀態轉移方程

    第i件物品的重量是w[i],價值是v[i] 用dp[i][j]表示前i件物品放入一個承重為j的背包可以獲得的最大價值,狀態轉移方程為

    dp[i][j] = max{dp[i-1][j], dp[i-1][j-w[i]] + c[i]}

    dp[i][j] = max{不放第i件物品,放第i件物品}

    在這個題目中,承重和價值都是這個值的大小,因為上一次例子用到了壓縮數組的寫法,這次就換一種寫法,完全按照狀態轉移方程來

    public class Solution {public boolean canPartition(int[] nums) {int sum = 0;for (int i = 0; i < nums.length; i++) {sum += nums[i];}// 奇數直接falseif ((sum & 1) == 1) {return false;}int target = sum >> 1;int[][] dp = new int[nums.length][target + 1];for (int i = 0; i < nums.length; i++) {for (int j = 0; j <= target; j++) {if (j >= nums[i]) {if (i == 0) {dp[i][j] = nums[i];} else {dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);}}}}return dp[nums.length - 1][target] == target;} }

    感謝(づ ̄3 ̄)づ╭?~ 完整閱讀!

    希望對您有所幫助 (*^▽^*)

    總結

    以上是生活随笔為你收集整理的动态顺序字符串基本操作实验_掌握套路,你也会用动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 亚洲福利在线播放 | av网在线播放 | 免费av网站在线观看 | 国产aaa视频 | 亚洲综合欧美日韩 | 大色网小色网 | 亚洲精品一卡 | 色先锋在线 | 911精品 | 青青草国产在线 | 亚洲国产精品久久久久爰性色 | 日日不卡av | 殴美一级黄色片 | 中国一区二区视频 | 黄色国产视频 | 少妇免费看 | 麻豆午夜视频 | а√天堂www在线天堂小说 | 成人久久网 | 亚洲AV无码一区二区三区蜜桃 | 91av视频播放| www.好吊色| 免费一级特黄特色大片 | 午夜久久久 | 高h教授1v1h喂奶 | 一本大道久久a久久精二百 琪琪色在线视频 | 一区不卡在线观看 | 青青草国内自拍 | 粗大黑人巨茎大战欧美成人 | 天堂俺去俺来也www久久婷婷 | 亚洲不卡视频在线 | 日本男女网站 | 狠狠干美女 | 99午夜| 一区二区三区精品免费视频 | 欧美一二三四五区 | 综合网视频| 图片区小说区视频区 | 日日碰狠狠添天天爽无码av | 老司机av导航 | 午夜时刻免费入口 | 亚洲精品在线一区二区 | 一边顶弄一边接吻 | 亚洲h视频在线观看 | a视频免费看 | 亚洲欧美视频一区 | 成人深夜免费视频 | 成人无高清96免费 | 国产福利片在线观看 | 中文字av| 欧美精品一二三四 | 日韩精品第三页 | 爱综合网 | www.日本com | 国产在线a视频 | 久久久精品人妻一区二区三区色秀 | 欧美精品中文 | 国产午夜精品久久久久 | 国产黑丝在线观看 | 日本大尺度电影免费观看全集中文版 | 影音先锋蜜桃 | 前任攻略在线观看免费完整版 | 粉嫩av国产一区二区三区 | 国产一区二区免费看 | 澳门久久 | 日本极品少妇 | 二区三区在线视频 | 国产美女永久无遮挡 | 青娱乐国产在线视频 | 先锋影音色| 狠狠一区 | 小萝莉末成年一区二区 | www欧美精品 | 亚洲天堂2015 | 国产麻豆一精品一av一免费 | 精品久久久影院 | 麻豆com| 亚洲精品97久久 | 轻点好疼好大好爽视频 | 中文字幕日韩精品无码内射 | 中文字幕欧美亚洲 | 噼里啪啦高清 | 国产三级做爰高清在线 | 国产无遮挡免费观看视频网站 | 我和我的太阳泰剧在线观看泰剧 | 女人高潮潮呻吟喷水 | 国产精品久久久久久久久免费桃花 | 91狠狠干 | 亚洲第九十九页 | 轻轻草在线视频 | 精品一区二区三区免费看 | 九九热在线精品视频 | 亚洲天堂网站在线 | 一级国产视频 | 捆绑无遮挡打光屁股 | 欧日韩在线观看 | 欧美日韩在线二区 | 一区二区三区四区五区六区 | 中国黄色网址 |