一個認為一切根源都是“自己不夠強”的INTJ
個人主頁:用哲學編程-CSDN博客
專欄:每日一題——舉一反三
Python編程學習
Python內置函數
Python-3.12.0文檔解讀
目錄
我的寫法
專業點評
優點
改進建議
時間復雜度分析
空間復雜度分析
總結
我要更強
方法一:優化空間復雜度(保留前綴和)
方法二:直接使用距離數組進行查詢
代碼優化解釋
時間和空間復雜度分析
總結
哲學和編程思想
編程思想和哲學
總結
舉一反三
1. 空間換時間
2. 時間換空間
3. 貪心思想
4. 分治思想
5. 動態規劃
舉一反三的技巧
https://pintia.cn/problem-sets/994805342720868352/exam/problems/type/7?problemSetProblemId=994805435700199424&page=0
我的寫法
# 從輸入中讀取一行,將其拆分為整數,并存儲在 distances 列表中
distances = list(map(int, input().split()))# 第一個元素是 N,表示接下來行程的數目
N = distances[0]# 剩下的元素是各段行程的距離
distances = distances[1:]# 從輸入中讀取 M,表示要處理的查詢次數
M = int(input())# 創建一個前綴和數組,用于存儲從起點到各位置的累計距離
prefix_sum = [0] * (N + 1)# 計算前綴和數組
for i in range(1, N + 1):# prefix_sum[i] 表示從起點到第 i 段行程結束的累計距離prefix_sum[i] = prefix_sum[i - 1] + distances[i - 1]# 總的環形路徑的距離
total_distance = prefix_sum[N]# 處理每個查詢
for i in range(M):# 讀取查詢的兩個點 a 和 ba, b = map(int, input().split())# 確保 a 小于 b,如果不是則交換 a 和 bif a > b:a, b = b, a# 計算從 a 到 b 的路徑距離distance1 = prefix_sum[b - 1] - prefix_sum[a - 1]# 計算從 b 到 a(繞過環的另一邊)的路徑距離distance2 = total_distance - distance1# 輸出較短的路徑距離print(min(distance1, distance2))
這段代碼的功能是處理一個環形路徑的查詢問題,計算兩個節點之間的最短距離。以下是對這段代碼的點評,以及時間復雜度和空間復雜度的分析。
專業點評
優點
- 前綴和的使用:
- 通過前綴和數組?prefix_sum,可以在常數時間內計算任意兩點之間的路徑距離。這種方法有效地將路徑長度查詢的時間復雜度從線性降到常數。
- 處理環形路徑:
- 計算兩種可能的路徑(順時針和逆時針)并取最小值的方式,確保了找到兩點間的最短路徑。
- 輸入處理:
- 代碼通過?map?和?split?組合高效地讀取并轉換輸入,符合常見的Python代碼風格。
改進建議
- 輸入驗證:
- 沒有對輸入進行驗證假設輸入是正確的。可以增加對輸入數據的合法性檢查,確保輸入值在合理范圍內。
- 變量命名:
- 可以通過更具描述性的變量名(如?distance_clockwise?和?distance_counterclockwise)來提高代碼的可讀性。
時間復雜度分析
- 前綴和計算:
- 構建前綴和數組的時間復雜度為?O(N),因為需要遍歷距離數組一次。
- 查詢處理:
- 每次查詢的時間復雜度為?O(1),因為計算路徑距離和比較最小值的操作都是常數時間內完成的。
- 總體時間復雜度:
- 總體時間復雜度為?O(N+M),其中?N?是距離數組的大小,M?是查詢次數。
空間復雜度分析
- 前綴和數組:
- 需要一個大小為?N+1?的前綴和數組?prefix_sum,空間復雜度為?O(N)。
- 輸入存儲:
- distances?數組存儲每段行程的距離,空間復雜度為?O(N)。
- 總體空間復雜度:
- 總的空間復雜度為?O(N),因為額外使用的主要是?distances?和?prefix_sum?數組。
總結
這段代碼使用前綴和技巧有效地解決了環形路徑的最短距離查詢問題,時間復雜度為 O(N+M),空間復雜度為O(N)。整體實現高效、簡潔,但可以通過增加輸入驗證和更清晰的變量命名進一步提高代碼質量。
我要更強
在這個問題中,由于我們需要進行多次查詢,而每次查詢都需要計算兩點之間的最短距離,前綴和的使用已經是一種非常高效的方案。進一步優化時間復雜度已經較為困難,因為查詢已經是 O(1) 的時間復雜度。
然而,我們可以考慮優化空間復雜度,特別是當 N 非常大時。以下是幾種可能的優化方法:
方法一:優化空間復雜度(保留前綴和)
盡管前綴和數組的空間復雜度是 O(N),但它已經是當前任務的較優解。如果我們希望進一步減少空間消耗,可以考慮在查詢中實時計算部分前綴和,而不是存儲整個前綴和數組。然而,由于查詢次數 M 通常大于路徑段數 N,這種方法可能并不會帶來實際的性能提升。
方法二:直接使用距離數組進行查詢
如果我們不使用前綴和數組,而是直接從距離數組中計算路徑長度,雖然會增加每次查詢的時間復雜度,但可以節省空間。
下面是直接使用距離數組進行查詢的代碼示例:
# 讀取輸入
distances = list(map(int, input().split()))
N = distances[0]
distances = distances[1:]
M = int(input())# 計算總距離
total_distance = sum(distances)# 處理每個查詢
for _ in range(M):a, b = map(int, input().split())if a > b:a, b = b, a# 計算從 a 到 b 的路徑距離distance1 = sum(distances[a-1:b-1])# 計算從 b 到 a(繞過環的另一邊)的路徑距離distance2 = total_distance - distance1# 輸出較短的路徑距離print(min(distance1, distance2))
代碼優化解釋
- 輸入讀取和初始化:
- 讀取并解析輸入,distances?包含各段行程的距離。
- 總距離計算:
- 計算環形路徑的總距離。
- 查詢處理:
對每個查詢,計算從?a?到?b?的兩種路徑距離,輸出較小的值。
時間和空間復雜度分析
- 時間復雜度:
- 每次查詢的時間復雜度為?O(N),因為我們需要計算部分路徑的距離。總的時間復雜度為?O(M?N)。
- 空間復雜度:
- 這種方法的空間復雜度為?O(1),除了輸入數據外不需要額外的空間。
總結
直接使用距離數組進行查詢可以顯著減少空間消耗,但會增加查詢的時間復雜度。如果路徑段數 N 較小且查詢次數 M 較多,這種方法可能不如使用前綴和的方案高效。選擇何種方法需要根據具體的輸入規模進行權衡。
哲學和編程思想
在上述解決問題的過程中,運用了多種編程思想和哲學,下面詳細說明:
編程思想和哲學
- 空間換時間(前綴和方法):
- 思想:通過增加額外的空間(前綴和數組)來換取時間上的優化。
- 具體應用:在前綴和方法中,我們使用一個大小為?�+1N+1?的數組來存儲從起點到各位置的累計距離。這使得每次查詢的時間復雜度從可能的?�(�)O(N)?降至?�(1)O(1)。
- 哲學:用更多的空間來預處理數據,以便在后續查詢中能夠快速得到結果。這種方法常用于需要多次查詢的大數據集。
- 時間換空間(直接計算路徑距離):
- 思想:減少空間使用,通過增加計算時間來實現同樣的功能。
- 具體應用:在沒有使用前綴和的情況下,我們直接在每次查詢中計算部分路徑的距離。這樣做雖然節省了空間,但查詢時間復雜度增加到了?�(�)O(N)。
- 哲學:在空間資源緊張的情況下,可以通過增加計算時間來節省空間。這種方法適用于空間資源比時間資源更寶貴的場景。
- 貪心思想:
- 思想:在每一步選擇中做出局部最優的選擇,希望最終得到全局最優解。
- 具體應用:在環形路徑的查詢中,我們計算了兩種路徑(順時針和逆時針)的距離,并選擇了最小的那個距離。這個過程實際上是一個貪心選擇的過程。
- 哲學:通過一系列局部最優的選擇,可以達到全局最優的效果。這種方法適用于問題可以分解成一系列子問題,并且每個子問題的最優解能構成問題的最優解的情況。
- 分治思想:
- 思想:將問題分解成更小的子問題,分別解決這些子問題,然后合并結果。
- 具體應用:雖然在上述代碼中沒有直接應用分治法,但前綴和的計算過程可以看作是一種分治思想的應用。我們先解決小范圍內的求和問題,再通過累積的方式解決更大范圍的求和問題。
- 哲學:通過將復雜問題分解成更小、更易解決的子問題,可以有效地降低解決問題的復雜度。這種方法尤其適用于遞歸和動態規劃。
- 動態規劃思想:
- 思想:通過記錄子問題的解,避免重復計算,逐步解決更大的問題。
- 具體應用:前綴和數組的構建和使用實際上是一種動態規劃思想的體現。我們通過記錄每個位置的累計和,避免了在每次查詢時重復計算相同的子區間和。
- 哲學:通過存儲和利用子問題的解,可以大大提高解決問題的效率。這種方法適用于具有重疊子問題的場景。
總結
通過分析和解釋,可以看到編程思想和哲學在解決實際問題中的應用。這些思想幫助我們在不同的約束條件下(如時間復雜度和空間復雜度)找到適合的解決方案。理解并靈活應用這些思想,可以使我們在編程中更加從容地應對各種挑戰。
舉一反三
理解和靈活應用編程思想和哲學是提升編程能力的關鍵。下面是一些技巧,結合這些思想,能更好地舉一反三,解決類似的問題:
1. 空間換時間
- 技巧:預處理和緩存
- 應用:當你需要頻繁查詢某些數據時,考慮是否可以通過預處理將結果存儲在數組或表中,從而減少每次查詢的計算量。
- 示例:前綴和、動態規劃中的狀態轉移表、緩存最近查詢結果的LRU緩存。
- 延伸:存儲中間結果
- 應用:在遞歸問題中,存儲已經計算過的結果,避免重復計算。
- 示例:斐波那契數列的動態規劃實現。
2. 時間換空間
- 技巧:實時計算
- 應用:當空間有限時,考慮是否可以在每次需要時實時計算所需的數據,而不是提前存儲所有可能的結果。
- 示例:對每次查詢實時計算路徑距離,而不是使用前綴和數組。
- 延伸:按需加載
- 應用:在處理大數據時,可以考慮按需加載需要的數據,而不是一次性加載所有數據。
- 示例:分頁加載數據、流式處理大文件。
3. 貪心思想
- 技巧:每次選擇局部最優解
- 應用:當問題可以分解成一系列子問題,并且每個子問題的最優解能構成問題的最優解時,考慮使用貪心算法。
- 示例:找零錢問題、活動選擇問題、最小生成樹(Prim和Kruskal算法)。
- 延伸:構建貪心策略
- 應用:在實際問題中,嘗試構建一個簡單的貪心策略,驗證其是否能得到全局最優解。
- 示例:為旅行商問題構建最近鄰居貪心策略。
4. 分治思想
- 技巧:將問題分解成更小的子問題
- 應用:當問題可以遞歸地分解成更小的問題,并且這些子問題獨立求解后可以合并成最終結果時,考慮使用分治算法。
- 示例:二分搜索、歸并排序、快速排序。
- 延伸:尋找合適的分解點
- 應用:在實際問題中,嘗試找到合適的分解點,以便將問題分解成更小的、易于解決的子問題。
- 示例:在圖算法中,將圖分解成子圖進行處理。
5. 動態規劃
- 技巧:記錄子問題的解
- 應用:當問題具有重疊子問題結構時,通過記錄子問題的解,避免重復計算。
- 示例:背包問題、最長公共子序列、最長遞增子序列。
- 延伸:自底向上和自頂向下
- 應用:理解動態規劃的兩種實現方式:自頂向下的記憶化搜索和自底向上的遞推。
- 示例:斐波那契數列的兩種動態規劃實現。
舉一反三的技巧
- 類比法:將當前問題與已知問題進行類比,尋找相似之處,應用相同或類似的解決方法。
- 實踐:在面對新問題時,問自己:“這是否類似于我之前解決過的某個問題?”
- 分解法:將復雜問題分解成更小的子問題,分別解決這些子問題,再將其組合成最終解決方案。
- 實踐:在解決復雜問題時,嘗試運用分治思想,將問題逐步細化。
- 驗證法:假設某種策略是可行的,先嘗試解決子問題,驗證其可行性,再逐步擴展到更大的問題。
- 實踐:在嘗試貪心算法或其他策略時,先在小規模問題上驗證,再擴展到全局。
- 優化法:在已有解決方案的基礎上,思考是否有可以優化的部分,特別是時間復雜度和空間復雜度。
- 實踐:在已有的解決方案上,問自己:“是否有更快(時間)或更省空間的方法?”
通過運用這些技巧和思想,可以更好地理解和解決問題,提升編程能力。理解背后的哲學和原理,能夠在面對新的或復雜的問題時,迅速找到有效的解決方案。