【左神算法刷題班】第18節:漢諾塔問題、島嶼問題、最大路徑和問題

第18節

題目1:漢諾塔問題(變體)

體系學習班18節有講暴力遞歸的漢諾塔原題。

給定一個數組arr,長度為N,arr中的值只有1,2,3三種

arr[i] == 1,代表漢諾塔問題中,從上往下第i個圓盤目前在左

arr[i] == 2,代表漢諾塔問題中,從上往下第i個圓盤目前在中

arr[i] == 3,代表漢諾塔問題中,從上往下第i個圓盤目前在右

那么arr整體就代表漢諾塔游戲過程中的一個狀況

如果這個狀況不是漢諾塔最優解運動過程中的狀況,返回-1

如果這個狀況是漢諾塔最優解運動過程中的狀況,返回它是第幾個狀況(而不是還剩幾個狀況)

思路

對于傳統的漢諾塔問題,如果我要將 123456 從最左邊的柱子上移動到最右邊的柱子上,需要分成三大步:

  1. 【第一大步】將 12345 從左邊的柱子移動到中間的位置
  2. 【第二大步】將 6 從左邊的柱子移動到右邊的位置
  3. 【第三大步】將 12345 從中間的位置移動到右邊的位置

上述傳統問題的解法是,定義遞歸函數 f(i, from, to, other),表示將 [0~i] 的圓盤從 from 柱子移動到 to 柱子上,另外那個柱子叫 other。

對于本題,需要明確一下題意,有幾個已知條件:

  1. 漢諾塔問題,最優解是唯一的路徑。
  2. 題目中給的過程狀態,如果不在唯一路徑上,就返回-1。
  3. 舉個極端的例子,1層漢諾塔問題,把1從最左邊的柱子上移動到最右邊的柱子上,只要一步就可以了。而”1在中間這個柱子上“這個狀態,就是一個不在最優解路徑上的例子。

本題的解法是,定義遞歸函數int step(int[] arr, int i, int from, int to, int other),表示當前來到 arr 狀態下,將 [0~i] 的圓盤從 from 柱子移動到 to 柱子上,另外那個柱子叫 other,返回至少需要幾步。

public static int kth(int[] arr) {int N = arr.length;return step(arr, N - 1, 1, 3, 2);
}// 我的疑問:arr為什么全程不更新?
// 自問自答:因為返回它當前走到了第幾個狀況,而不是還剩幾個狀況。
public static int step(int[] arr, int index, int from, int to, int other) {if (index == -1) {return 0;}if (arr[index] == other) { // 最大的數字只可能在from或者to的底部,不可能在other上return -1;}// arr[index]的值,剩下兩種情況:// 情況1:arr[index] == from// 情況2:arr[index] == toif (arr[index] == from) { // 情況1:如果index號圓盤還在from上,說明上述連【第一大步】都沒走完// 因為我只想知道當前已經走過多少步了,所以只要統計在【第一大步】中走了多少步就可以了,后面的【第二大步】【第三大步】肯定根本沒走// 怎么統計呢?我們知道【第一大步】的目標是將[0~i-1]從from挪到other上,而且當前已經走到arr狀態了,所以就這樣繼續遞歸return step(arr, index - 1, from, other, to);} else { // 情況2:如果index號圓盤已經在to上了,說明已經完成[0~index-1]的漢諾塔問題了// 【第一大步】已經完成的從[0~index-1]范圍上的index層漢諾塔問題需要的步驟數(n層漢諾塔,最優解2^n-1步)int p1 = (1 << index) - 1; // 【第二大步】已經將index號圓盤從from挪到to了,因為我們從arr中看到index號圓盤已經在to上了int p2 = 1; // 【第三大步】當前正在經歷的,將[0~i-1]號圓盤從other挪到to上,在arr狀態下,統計已經走過多少步了?int p3 = step(arr, index - 1, other, to, from); // 如果發現它的子問題根本就不是最優解的某一步,直接返回-1if (p3 == -1) { return -1;}return p1 + p2 + p3;}
}
image-20230814002726237

題目2:兩個島嶼的距離,“感染”問題

Leetcode 原題:

https://leetcode.com/problems/shortest-bridge/

我在力扣上的自己寫的答案:

class Solution {int m, n;public static final int offset = 100;public int shortestBridge(int[][] grid) {m = grid.length;n = grid[0].length;// 將其中一個島A加offset,用來區分兩個島label:for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == 1) {incr(grid, i, j);break label; // 中斷所有循環,回到label處,但并不重新進入循環}}}// 左上角主動感染,右下角原地不動int term = offset;while (true) {boolean[][] seen = new boolean[m][n];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == term && !seen[i][j]) {int result = process(grid, i, j, term, seen);if (result != Integer.MAX_VALUE) return result - offset;}}}term++;}}// 當前島嶼向外感染public int process(int[][] grid, int i, int j, int term, boolean[][] seen) {int result = Integer.MAX_VALUE;if (i < 0 || i == m || j < 0 || j == n || seen[i][j]) return result; // 越界,或重復路線seen[i][j] = true;if (grid[i][j] == 0) { // 當前位置未感染,則感染grid[i][j] = term + 1;} else if (grid[i][j] == term) { // 當前位置是感染源,則去感染周圍result = Math.min(result, process(grid, i + 1, j, term, seen));result = Math.min(result, process(grid, i - 1, j, term, seen));result = Math.min(result, process(grid, i, j + 1, term, seen));result = Math.min(result, process(grid, i, j - 1, term, seen));} else if (grid[i][j] == 1) { // 兩島接壤,則快速返回return term;}return result;}// 給其中一個島加offsetpublic void incr(int[][] grid, int i, int j) {if (i < 0 || i == m || j < 0 || j == n) return;if (grid[i][j] == 1) {grid[i][j] = offset;incr(grid, i + 1, j);incr(grid, i - 1, j);incr(grid, i, j + 1);incr(grid, i, j - 1);}}
}

題目3:最大路徑和

牛客網原題:

https://www.nowcoder.com/questionTerminal/8ecfe02124674e908b2aae65aad4efdf

給定一個矩陣matrix,先從左上角開始,每一步只能往右或者往下走,走到右下角。然后從右下角出發,每一步只能往上或者往左走,再回到左上角。任何一個位置的數字,只能獲得一遍。返回最大路徑和。

輸入描述
第一行輸入兩個整數M和N,M,N<=200
接下來M行,每行N個整數,表示矩陣中元素5 10
1 1 1 1 1 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 0
0 0 0 0 1 1 1 1 1 1
輸出描述
輸出一個整數,表示最大路徑和16
思路

第一次見到這題,是在體系學習班第14節。當時只講了不能貪心,應該用dp,但沒有細說。

黃色部分表示我想要拿到的位置:

在這里插入圖片描述

錯誤的貪心路徑

少拿一個灰色的格子。
在這里插入圖片描述

正確的路徑

最好情況下,能夠拿到所有的格子。

在這里插入圖片描述

雖然題目要求是一來一回,但我們可以想象成有兩個人 a、b,都從左上角走到右下角,求整個過程中,最多能拿到多少值。

內存超限版本如下。其實可以省掉一個維度就不會超了,因為(i1, j1), (i2, j2) 兩個坐標中,存在關系:i1+j1=i2+j2。可變參數數量能省則省!

import java.util.Scanner;// 注意類名必須為 Main, 不要有任何 package xxx 信息
public class Main {static int[][] arr;public static void main(String[] args) {Scanner in = new Scanner(System.in);int m = in.nextInt();int n = in.nextInt();arr = new int[m][n];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {arr[i][j] = in.nextInt();}}int[][][][] dp = new int[m][n][m][n];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {for (int k = 0; k < m; k++) {for (int l = 0; l < n; l++) {dp[i][j][k][l] = -1;}}}}int res = process(0, 0, 0, 0, dp);System.out.println(res);}// 當前a在i1,j1位置,b在i2,j2位置// 兩個人都只能向右走或者向下走,求能拿到的最多點數public static int process(int i1, int j1, int i2, int j2, int[][][][] dp) {if (i1 == arr.length || j1 == arr[0].length) return 0;if (i2 == arr.length || j2 == arr[0].length) return 0;if (dp[i1][j1][i2][j2] >= 0) return dp[i1][j1][i2][j2];// a,b如果走到了同一個位置,點數只能累加一次int res = arr[i1][j1];if (i1 != i2 && j1 != j2) res += arr[i2][j2];// a向右,b向右int p1 = process(i1 + 1, j1, i2 + 1, j2, dp);// a向下,b向下int p2 = process(i1, j1 + 1, i2, j2 + 1, dp);// a向右,b向下int p3 = process(i1, j1 + 1, i2 + 1, j2, dp);// a向下,b向右int p4 = process(i1 + 1, j1, i2, j2 + 1, dp);res += Math.max(Math.max(p1, p2), Math.max(p3, p4));dp[i1][j1][i2][j2] = res;return res;}
}
/*5 10
1 1 1 1 1 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 0
0 0 0 0 1 1 1 1 1 12 2
1 1
1 1*/

題目4

牛客網原題:

https://www.nowcoder.com/practice/7201cacf73e7495aa5f88b223bbbf6d1

給定兩個有序數組arr1和arr2,再給定一個整數k,你可以從來自arr1和arr2的兩個數各選一個數,返回相加和最大的前k個。

思路

不能用雙指針從最右邊開始往左滑動,因為這樣會丟失本來可以重復使用的數字。

正確的方法是用大根堆。

當從大根堆拿走一個元素之后,將表格中在它左邊和上邊的元素,加入大根堆。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/38132.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38132.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38132.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Terraform 系列-批量創建資源時如何根據某個字段判斷是否創建

系列文章 Terraform 系列文章Grafana 系列文章 概述 前文 Grafana 系列 - Grafana Terraform Provider 基礎 介紹了使用 Grafana Terraform Provider 創建 Datasource. 這幾天碰到這么一個現實需求&#xff1a; 使用 Terraform 批量創建日志數據源時, 有的數據源類型是 El…

MongoDB 數據庫詳細介紹

MongoDB 數據庫詳細介紹 MongoDB&#xff08;來自“Humongous”&#xff0c;意為巨大的&#xff09;是一個開源、高性能、無模式&#xff08;NoSQL&#xff09;、文檔導向的分布式數據庫。它以其靈活性、可擴展性和強大的查詢功能而聞名于世。MongoDB 使用 JSON 格式的文檔來存…

主從同步介紹、主從同步原理、主從同步結構、構建思路、配置一主一從、配置一主多從、讀寫分離介紹、工作原理、配置mycat服務、添加數據源、創建集群、指定主機角

Top NSD DBA DAY07 案例1&#xff1a;MySQL一主一從案例2&#xff1a;配置一主多從結構案例3&#xff1a;數據讀寫分離 1 案例1&#xff1a;MySQL一主一從 1.1 問題 數據庫服務器192.168.88.53配置為主數據庫服務器數據庫服務器192.168.88.54配置為從數據庫服務器客戶端192…

網絡編程(8.14)TCP并發服務器模型

作業&#xff1a; 1. 多線程中的newfd&#xff0c;能否修改成全局&#xff0c;不行&#xff0c;為什么&#xff1f; 2. 多線程中分支線程的newfd能否不另存&#xff0c;直接用指針間接訪問主線程中的newfd,不行&#xff0c;為什么&#xff1f; 多線程并發服務器模型原代碼&…

排查docker無法啟動問題

查看Linux系統操作日志(最后200行就可以排查)&#xff1a; tail -200f /var/log/messages

數據分析--帆軟報表--大數據大屏

進入國企公司學習有一段時間了&#xff0c;崗位是數據分析方向------ 母前使用的是帆軟工具進行的開發。 可以進行大數據大屏 也可使嵌入到手機端。 下面是例子

Python-OpenCV中的圖像處理-GrabCut算法交互式前景提取

Python-OpenCV中的圖像處理-GrabCut算法交互式前景提取 Python-OpenCV中的圖像處理-GrabCut算法交互式前景提取 Python-OpenCV中的圖像處理-GrabCut算法交互式前景提取 cv2.grabCut(img: Mat, mask: typing.Optional[Mat], rect, bgdModel, fgdModel, iterCount, mode…) img…

數據庫連接池

什么是數據庫連接池 使用數據庫連接池的好處是減少了連接的創建和關閉的開銷&#xff0c;提高了數據庫訪問的性能和效率。 為什么我們要使用數據庫連接池 我們使用數據庫連接池的主要原因是為了提高應用程序訪問數據庫的性能和效率。使用數據庫連接池的好處: 連接重用&…

【Apple】Logic Pro導入7.1.4.wav并自動分析多聲道

Step1: 創建空項目 Step2: 選中下圖“使用麥克風或...”這一項&#xff0c;底下要創建的軌道數填1就行。 點擊創建之后&#xff1a; Step3: 拖動文件、拖動文件、拖動文件到項目中&#xff0c;并選中復選框“所有所選文件都源自一個項目&#xff08;將創建一個智能速度多軌道集…

[NLP]LLM 訓練時GPU顯存耗用量估計

以LLM中最常見的Adam fp16混合精度訓練為例&#xff0c;分析其顯存占用有以下四個部分&#xff1a; GPT-2含有1.5B個參數&#xff0c;如果用fp16格式&#xff0c;只需要1.5G*2Byte3GB顯存, 但是模型狀態實際上需要耗費1.5B*1624GB. 比如說有一個模型參數量是1M&#xff0c;在…

什么是前端框架?怎么學習? - 易智編譯EaseEditing

前端框架是一種用于開發Web應用程序界面的工具集合&#xff0c;它提供了一系列預定義的代碼和結構&#xff0c;以簡化開發過程并提高效率。 前端框架通常包括HTML、CSS和JavaScript的庫和工具&#xff0c;用于構建交互式、動態和響應式的用戶界面。 學習前端框架可以讓您更高效…

nginx的負載均衡

nginx的負載均衡 文章目錄 nginx的負載均衡1.以多臺虛擬機作服務器1.1 在不同的虛擬機上安裝httpd服務1.2 在不同虛擬機所構建的服務端的默認路徑下創建不同標識的文件1.3 使用windows本機的瀏覽器分別訪問3臺服務器的地址 2.在新的一臺虛擬機上配置nginx實現反向代理以及負載均…

使用element UI 的el-upload上傳圖片并攜帶參數的用法

直接看代碼&#xff1a;前端實現 <div class"upload"><el-uploadclass"upload-demo"name"upload_name":data"{user_name:user_name}"action"http://localhost:8000/api/deal_pest_Image":show-file-list"fal…

vb+sql汽車配件管理系統設計與實現

摘 要 目前汽車配件銷售企業大多數在其連鎖店的管理還是手工進行,隨著汽車配件行業的迅速發展,手工管理的種種弊端暴露無疑,給銷售企業的發展帶來了不必要的麻煩。為了規范企業內部管理,提高企業業務管理水平,更好的為客戶服務,應采用計算機來管理汽車配件的進銷存業務。…

【Sklearn】基于樸素貝葉斯算法的數據分類預測(Excel可直接替換數據)

【Sklearn】基于樸素貝葉斯算法的數據分類預測(Excel可直接替換數據) 1.模型原理2.模型參數3.文件結構4.Excel數據5.下載地址6.完整代碼7.運行結果1.模型原理 模型原理: 樸素貝葉斯分類是基于貝葉斯定理的一種分類方法。它假設特征之間相互獨立(樸素性),從而簡化計算過…

01|Java中常見錯誤或不清楚

補充&#xff1a;length vs length() vs size() 1 java中的length屬性是針對數組說的,比如說你聲明了一個數組,想知道這個數組的長度則用到了length這個屬性. 2 java中的length()方法是針對字符串String說的,如果想看這個字符串的長度則用到length()這個方法. 3.java中的siz…

【Vue-Router】命名視圖

命名視圖 同時 (同級) 展示多個視圖&#xff0c;而不是嵌套展示&#xff0c;例如創建一個布局&#xff0c;有 sidebar (側導航) 和 main (主內容) 兩個視圖&#xff0c;這個時候命名視圖就派上用場了。 可以在界面中擁有多個單獨命名的視圖&#xff0c;而不是只有一個單獨的出…

Python獲取、修改主機名稱和IP地址實踐

Python獲取、修改主機名稱和IP地址的方法有多種&#xff0c;內置socket模塊、執行系統命令、第三方模塊等等&#xff0c;本文只是完成功能的一次成功的實踐。 1. 獲取、修改主機名稱 本案例使用python的socket模塊獲取、修改主機名稱&#xff0c;socket模塊是一個用于實現網絡…

UML-A 卷-知識考卷

UML-A 卷-知識考卷 UML有多少種圖&#xff0c;請列出每種圖的名字&#xff1a; 常用的幾種UML圖&#xff1a; 類圖&#xff08;Class Diagram&#xff09;&#xff1a;類圖是描述類、接口、關聯關系和繼承關系的圖形化表示。它展示了系統中各個類之間的靜態結構和關系。時序…

TFRecords詳解

內容目錄 TFRecords 是什么序列化(Serialization)tf.data 圖像序列化&#xff08;Serializing Images)tf.Example函數封裝 小結 TFRecords 是什么 TPU擁有八個核心&#xff0c;充當八個獨立的工作單元。我們可以通過將數據集分成多個文件或分片&#xff08;shards&#xff09;…