算法-對列表元素劃分成兩個和值最大且相等的子列表

現有私募基金發行一支特殊基金產品,該基金認購人數上限不超過 30 人, 募集總金額不超過 3000W,每個投資人認購金額不定。該基金只能將募集到的錢用于投資兩支股票,且要求兩支股票投資金額必須相同,且每位投資人的錢只能用于投資一支股票或不投資。問如何在給定募集條件下,實現投資金額最大化。如果無法實現則返回0

在這里插入圖片描述

解題方法

  • 第1步:找到和為偶數的所有子集
  • 第2步:將子集傳算法計算:兩子集相等
  • 第3步:篩選出和值最大的結果

在這里插入圖片描述

注意:
[1, 2, 3, 10, 5, 5], 只要求子集和為總和一半,不管哪種劃分方式,都是“最優解”

a: [1, 2 10], [3, 5, 5]

b: [1, 2, 5, 5], [3, 10]

一、代碼與執行結果

具體代碼

1、動態規劃:將列表切分成兩個和值相等的子列表

import random# 動態規劃:將列表切分成兩個和值相等的子列表def can_partition(nums):total_sum = sum(nums)if total_sum % 2 != 0:return Falseif len(nums) == 2: if nums[0] == nums[1]:return ([nums[0]], [nums[1]], nums[0]*2)else:return Falsetarget_sum = total_sum // 2n = len(nums)# ----------------------# 初始化動態規劃表 dp,其中 dp[i][j] 表示前 i 個元素中是否存在和為 j 的子集。dp = [[False] * (target_sum + 1) for _ in range(n + 1)] # 空間: O(n * target_sum)# 初始時,將 dp[0][0] 設為 True。dp[0][0] = True# 創建一個字典 index_map,用于記錄每個元素在原列表中的索引。[1, 5, 11, 5] [1, 5, 5, 11]index_map = {}for i in range(n):if nums[i] not in index_map:index_map[nums[i]] = [i]else:index_map[nums[i]].append(i)# 哈希表# 使用兩層循環遍歷元素和目標和的可能取值,更新動態規劃表 dp。# 對于每個位置 (i, j),如果前 i-1 個元素中存在和為 j 的子集,# 或者前 i-1 個元素中存在和為 j-nums[i-1] 的子集且第 i 個元素沒有被選擇,# 則 dp[i][j] 為 True。for i in range(1, n + 1): # O(n)for j in range(target_sum + 1): # O(target_sum) -> O(n * target_sum)dp[i][j] = dp[i - 1][j]if j >= nums[i - 1]:dp[i][j] = dp[i][j] or dp[i - 1][j - nums[i - 1]]# 如果動態規劃表中 dp[n][target_sum] 為 True,說明存在一種劃分方式,將列表劃分成兩個和相等的子列表。# 接下來,需要找到具體的劃分方式:if dp[n][target_sum]:# 初始化空列表 subset1 和 subset2_indices,subset1 用于存放第一個子列表的元素,subset2_indices 用于存放第二個子列表的索引。subset1, subset2_indices = [], []i, j = n, target_sum# 從動態規劃表的右下角開始向左上角遍歷,如果第 i 個元素沒有被選擇,則將其加入 subset1 中,同時更新目標和 j。while i > 0 and j > 0:# 如果 dp[i - 1][j] 為 True,說明第 i 個元素沒有被選擇,則將 i 減 1,即考慮前 i - 1 個元素。if dp[i - 1][j]:i -= 1# 如果 dp[i - 1][j] 為 False,說明第 i 個元素被選擇了,則將第 i 個元素加入 subset1 中,并更新目標和 j。else:subset1.append(nums[i - 1])j -= nums[i - 1]subset2_indices.append(i - 1)  # 更新 subset2_indicesi -= 1# 最終得到的 subset2_indices 中存放的是第二個子列表的索引,根據這些索引可以得到第二個子列表 subset2。subset2_indices = set(range(n)) - set(subset2_indices)subset2 = [nums[i] for i in subset2_indices]return subset1, subset2, sum(subset1)*2else:return False# 返回兩個子列表和相加的結果的兩倍,表示劃分成功,否則返回 False。# # Test
# nums = [1, 5, 11, 5]
# print(can_partition(nums))  # Output: ([11], [1, 5, 5])# nums = [1, 5, 5, 11]
# print(can_partition(nums))  # Output: ([11], [1, 5, 5])# nums = [1, 2, 2, 3, 4]
# print(can_partition(nums))  #  Output: ([3, 2, 1], [2, 4])

2、篩選出所有符合要求的子集

# 劃分子集:篩選出所有符合要求(例如和值為偶數、至少包含2個元素等)的子集,并降序排序def subset_sums(lst):total_sum = sum(lst)Len = len(lst)if (Len <= 1) or (Len > 30) or (total_sum > 3000):return Falsedef calc_sum(subset):return sum(subset)def is_even(num):return num % 2 == 0def find_sublists(lst, start, end, path, result): # # 回溯,時間, O(2^n)''' lst:要查找子列表的原始列表start:當前遞歸的起始索引end:遞歸的結束索引(不包括在內)path:當前已構建的子列表result:包含所有符合條件的子列表的列表'''if len(path) >= 2 and is_even(calc_sum(path)) and (len(path) > 2 or path[0] == path[1]):result.append(path.copy())# 遞歸終止條件,當起始索引等于結束索引時,遞歸結束if start == end:return# 遍歷從start到end的索引,每次將當前索引對應的元素添加到path中for i in range(start, end):path.append(lst[i])# 遞歸調用函數,將i+1作為新的起始索引,繼續尋找子列表find_sublists(lst, i + 1, end, path, result)# 在遞歸返回后,彈出最后一個元素,以便嘗試其他可能的子列表path.pop()def sort_and_remove_duplicates(lists): # # O(nlog(n))# 對每個子列表進行排序,并轉換為元組以便于后續去重 sorted_lists = [tuple(sorted(sublist)) for sublist in lists]# 使用集合去除重復的子列表unique_lists = list(set(sorted_lists))# 按照子列表的和值從大到小排序整個列表unique_lists.sort(key=lambda x: sum(x), reverse=True)# 將子列表轉換回列表形式并返回結果return [list(sublist) for sublist in unique_lists]def sort_sublists(lists):sorted_lists = sorted(lists, key=lambda x: calc_sum(x), reverse=True)for i in range(len(sorted_lists)):sorted_lists[i].sort()return sorted_listsresult = []find_sublists(lst, 0, len(lst), [], result)return sort_sublists(sort_and_remove_duplicates(result))

3、測試-生成模擬數據

# 生成模擬數據def split_number(number, parts):# 初始化每個部分為0parts_sum = [0] * partsremaining = number# 循環分配數字直到只剩下最后一個部分for i in range(parts - 1):# 隨機分配數字,確保剩余數字可以被均分parts_sum[i] = random.randint(1, remaining - parts + i + 1)remaining -= parts_sum[i]# 最后一個部分取得所有剩余數字parts_sum[-1] = remainingreturn parts_sumdef generate_random_partitions():# 隨機將 Q 兩次切分成不同的 M 份# M1 和 M2 的個數分別為 1 到 10 之間,這樣設置比其他方法產生的結果更加均衡,防止產生大量1M1 = random.randint(1, 10) M2 = random.randint(1, 10)Q_min = max(M1, M2)# 生成一個 30-1500 內的隨機數Q = random.randint(Q_min, 1500)# 隨機切分 Q 為 M1 份和 M2 份part1 = split_number(Q, M1)part2 = split_number(Q, M2)Randnumber = 30 - M1 + M2total_money = Q * 2balance = 3000 - total_moneybal = random.randint(1, balance)if Randnumber >= 1:R = random.randint(1, Randnumber)if R <= balance:part_balance = split_number(bal, R)else:part_balance = []else:part_balance = []return part1 + part2 + part_balance

4、主程序執行

if __name__ == "__main__":# 示例數據nums = [1, 5, 11, 5]# nums = [1, 5, 5, 11]# nums = [12,7,13,18]# nums = [12,8,13,18]# nums = [1,2,3,4,5,6]# nums = [2,3,4,5,6]# nums = [1,2,3,4,5]# nums = [17,3,8]# nums = [1,1]# nums = [1,2]# nums = [1, 2, 2, 3, 4]# nums = [2, 2, 2, 2, 3]print("示例數據-輸入:", nums)# 模擬數據# nums = generate_random_partitions()# print("模擬數據-輸入:", nums)numss = subset_sums(nums)if numss:# print("子集組合:", numss) # 子集組合,最優解只會在這些子集中產生for nums in numss:result = can_partition(nums)if result:print("輸出:", result) # 和值最大的兩個相等子集breakelse:print("輸出:", 0)

5、輸出

示例數據-輸入: [1, 5, 11, 5]
輸出: ([5, 5, 1], [11], 22)

具體算法過程:
在這里插入圖片描述

二、函數關鍵信息

  • 1、can_partition(nums)函數信息
    函數名稱:can_partition
    關鍵參數和含義:
    nums:輸入的整數列表,表示要判斷是否能將其分割成兩個和相等的子集。
    返回值:
    如果能將 nums 分割成兩個和相等的子集,則返回一個元組 (subset1, subset2, sum(subset1)*2),其中 subset1 和 subset2 分別為兩個和相等的子集,sum(subset1)*2 為兩個子集的和的兩倍。
    如果不能將 nums 分割成兩個和相等的子集,則返回 False。
    主要變量信息:
    total_sum:nums 列表所有元素的和。
    target_sum:total_sum 的一半,即要達到的子集的目標和。
    n:nums 列表的長度。
    dp:動態規劃數組,dp[i][j] 表示前 i 個元素是否能湊出和為 j 的子集。
    index_map:字典,用于記錄 nums 中每個元素的索引。
    subset1:和相等的子集之一。
    subset2_indices:和相等的另一個子集的索引。
    subset2:和相等的另一個子集。

  • 2、subset_sums(lst)函數信息
    函數名稱:subset_sums
    關鍵參數和含義:
    lst:輸入的整數列表,表示要找出其中所有符合條件的子集。
    返回值:
    如果找到符合條件的子集,則返回一個列表,列表中包含所有符合條件的子集,每個子集都是一個列表。
    如果未找到符合條件的子集,則返回 False。
    主要變量信息:
    total_sum:lst 列表所有元素的和。
    Len:lst 列表的長度。
    result:存儲符合條件的子集的列表。
    calc_sum(subset):計算子集 subset 的和。
    is_even(num):判斷一個數是否為偶數。
    find_sublists(lst, start, end, path, result):遞歸查找 lst 中符合條件的子集。
    sort_and_remove_duplicates(lists):對子集進行排序和去重。
    sort_sublists(lists):對子集列表進行排序。

三、程序設計原理

  • 1、整體思路
    第1步:對于一個列表找到和為偶數的所有子集,限制子集至少包含兩個元素,若只有兩個元素,要求元素值大小相等。對子集和值大小進行降序排序。
    第2步:將符合要求的子集用動態規劃算法進行切分,使得兩子集相等,一旦找到,便終止循環,此時的結果滿足和值最大
    第3步:生成模擬數據,檢驗算法有效性。

  • 2、can_partition(nums)函數具體方法
    2.1 函數思路
    首先計算nums數組的總和total_sum,如果total_sum為奇數,則無法分割成等和子集,直接返回False。
    然后計算target_sum為total_sum的一半,問題轉化為在nums數組中找到一組元素的和等于target_sum。
    使用動態規劃算法,定義二維數組dp[i][j]表示在前i個元素中是否存在子集的和為j。初始化dp數組為False,表示初始狀態下不存在子集的和為j。
    遍歷nums數組,對于每個元素nums[i],遍歷可能的和值j(從target_sum到nums[i]),更新dp[i][j]為True,表示在前i個元素中存在子集的和為j。
    最終如果dp[n][target_sum]為True,表示存在一組子集的和為target_sum,根據dp數組回溯找出這組子集。
    2.2 回溯查找子集:
    當dp[n][target_sum]為True時,從dp數組中回溯查找這組子集。從最后一個元素dp[n][target_sum]開始,如果dp[i-1][j]為True,則說明nums[i-1]不在子集中,將i減一;否則,將nums[i-1]加入subset1中,并將j減去nums[i-1],繼續向前查找。
    通過回溯找到subset1后,將剩余的元素構成subset2。
    2.3 優化空間:
    使用了index_map來記錄nums數組中每個元素的索引,避免重復計算。
    2.4 返回結果:
    如果存在分割成等和子集的方案,則返回subset1, subset2和sum(subset1)*2;否則返回False。

  • 3、subset_sums(lst)函數具體方法
    3.1、 函數思路
    首先計算lst數組的總和total_sum和長度Len,如果Len小于等于1或大于30,或者total_sum大于3000,則直接返回False。
    使用回溯算法,遞歸地尋找符合要求的子集,即和值為偶數、至少包含2個元素。
    對找到的子集進行排序和去重操作。
    3.2、回溯算法:
    定義一個輔助函數find_sublists(lst, start, end, path, result),其中lst為輸入數組,start和end表示當前搜索范圍的起始和結束位置,path為當前的子集,result為存儲符合要求的子集的列表。
    在回溯過程中,如果當前子集的長度大于等于2且和值為偶數且長度大于2或者前兩個元素相等,則將該子集加入結果中。
    遞歸搜索下一個元素加入子集或者不加入子集的情況,直到搜索完所有元素。
    3.3、排序和去重:
    對于符合要求的子集列表,首先對每個子集進行排序,然后轉換為元組以便于后續去重。
    使用集合去除重復的子集。
    最后按照子集的和值從大到小排序整個列表,并將子集轉換回列表形式返回結果。
    3.4、邊界條件處理:
    在開始時檢查輸入數組的長度和總和是否滿足條件,如果不滿足則直接返回False。

四、復雜度分析

  • 1、can_partition(nums)函數復雜度分析
    時間復雜度:程序使用了動態規劃算法來解決分割等和子集問題。外層循環的時間復雜度為O(n),內層循環的時間復雜度為O(target_sum),其中n為nums數組的長度,target_sum為nums數組所有元素的和的一半。因此,總體時間復雜度為O(n * target_sum)。
    空間復雜度:程序使用了一個二維數組dp來保存狀態,其大小為(n+1) * (target_sum+1),因此空間復雜度為O(n * target_sum)。

  • 2、subset_sums(lst)函數復雜度分析
    時間復雜度:程序使用了回溯算法來找出所有符合要求的子集,并對結果進行排序和去重。回溯算法的時間復雜度為O(2n),其中n為lst數組的長度。排序和去重操作的時間復雜度為O(nlog(n)),因此總體時間復雜度為O(2n + nlog(n))。
    空間復雜度:程序使用了遞歸棧來存儲回溯過程中的中間結果,最壞情況下的空間復雜度為O(n),其中n為lst數組的長度。

  • 3、整體復雜度
    can_partition 函數的時間復雜度是O(n * target_sum),空間復雜度也是O(n * target_sum)。subset_sums 函數的時間復雜度是O(2^n + nlog(n)),空間復雜度是O(n)。
    在整個代碼塊中,首先調用 subset_sums(nums) 函數得到子集組合 numss,這一步的時間復雜度是 subset_sums 的時間復雜度,即O(2^n + n
    log(n))。
    然后,對于 numss 中的每個子集,調用 can_partition(nums) 函數,這一步的時間復雜度是O(n * target_sum)。由于 numss 中可能有多個子集,所以整個過程中需要執行多次 can_partition 函數。
    綜上所述,整個代碼塊的總時間復雜度取決于兩個函數中較大的那個復雜度,即O(2^n + n*log(n)),空間復雜度為O(n * target_sum)。

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

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

相關文章

0X JavaSE-- 集合框架【Collection(List、Set、Queue)、Map】

每一個集合類底層采用的數據結構不同&#xff0c;例如ArrayList集合底層采用了數組&#xff0c;LinkedList集合底層采用了雙向鏈表&#xff0c;HashMap集合底層采用了哈希表&#xff0c;TreeMap集合底層采用了紅黑樹。**集合中存儲的是引用。**即。集合中存放的是對象的地址&am…

springboot報錯:Failed to start bean ‘documentationPluginsBootstrapper‘

項目場景&#xff1a; springboot項目啟動時報錯 問題描述 具體報錯信息&#xff1a; 可能原因分析&#xff1a; 1、SpringFox的版本與Spring Boot的版本不兼容。解決這個問題&#xff0c;你可能需要檢查你正在使用的SpringFox和Spring Boot的版本&#xff0c;確保它們是兼容…

一千題,No.0037(組個最小數)

給定數字 0-9 各若干個。你可以以任意順序排列這些數字&#xff0c;但必須全部使用。目標是使得最后得到的數盡可能小&#xff08;注意 0 不能做首位&#xff09;。例如&#xff1a;給定兩個 0&#xff0c;兩個 1&#xff0c;三個 5&#xff0c;一個 8&#xff0c;我們得到的最…

[AIGC] 使用Flink SQL統計用戶年齡和興趣愛好

Apache Flink是一個具有強大計算能力、高吞吐量、低延遲的分布式計算框架&#xff0c;它支持批計算和流計算。Flink SQL是Flink ecosystem的一部分&#xff0c;是一種對結構化數據進行批和流處理的聲明式語言。本文以一個簡單的實例講解如何使用Flink SQL來統計用戶年齡和興趣愛…

C# 面向對象編程(一)——類 第三篇

總目錄 C# 語法總目錄 系列鏈接 C# 面向對象編程(一) 類 第一篇 C# 面向對象編程(一) 類 第二篇 C# 面向對象編程(一) 類 第三篇 C# 面向對象編程 一 ——類 第三篇 簡介面向對象編程類 第三篇9. 重載運算符10. 分部方法** nameof方法 **** GetType 方法和 typeof方…

【Intro】Heterogeneous Graph Attention Network(HAN)

論文鏈接&#xff1a;https://arxiv.org/pdf/1903.07293 Abstract 異構性和豐富的語義信息給面向異構圖的圖形神經網絡設計帶來了巨大的挑戰。 -> 一種基于分層注意的異構圖神經網絡&#xff0c;包括節點級注意和語義級注意。具體來說&#xff0c;節點級關注旨在學習節點…

GPT4o還沒用上?落后一個月!

文章目錄 一.Share官方網站&#xff1a;以一半的價格享受官網服務1.1 網址1.2 一些介紹和教學實戰&#xff1a;1.3 主界面&#xff08;支持4o)&#xff1a;1.4 GPTS&#xff08;上千個工具箱任你選擇&#xff09;&#xff1a;1.5 快速的文件數據分析&#xff08;以數學建模為例…

一次“yarn Couldn‘t find package“問題的排查

本文記錄一次使用yarn install 時報錯 Couldn’t find package xxxx 問題的排查。 問題描述 問題來自于筆者對一個前端項目進行debug時的yarn install 報錯信息&#xff0c;在一個可以明確代碼沒有問題的項目中&#xff0c;因為切換環境&#xff0c;重新執行yarn install,發現…

qt qcomboBox實現自動檢索功能 通過輸入匹配字符進行篩選

本人做了一個自定義控件SeepedSearch 用于快速檢索匹配的字符的下拉框 方便查找目標 直接上源碼 1. SpeedSerach.h #pragma once #include class QComboBox; class QCompleter; class SpeedSearch : public QWidget { Q_OBJECT public: explicit SpeedSearch(QWidget *paren…

web前端三大主流框架指的是什么

web前端三大主流框架是什么&#xff1f;前端開發師的崗位職責有哪些&#xff1f;這邊整理了相關內容供大家參考了解&#xff0c;請各位小伙伴隨小編一起查閱下面的內容。 web前端三大主流框架 web前端三大主流框架是Angular、React、Vue。 1.Angular Angular原名angularJS誕生…

如何用python做一個貪吃蛇程序?——潯川AI社(VIP)

1 游戲說明: 死亡條件:碰壁、吃自己! 狀態:只有吃了食物才會隨機生成其中一種狀態,分別是:穩如老狗、幸運光滑、衰神附體之一 狀態:穩如老狗:相對于上一次速度不變! 狀態:幸運光滑:相對于上一次速度變慢! 狀態:衰神附體:相對于上一次速度變快! 總體速率對比…

UnityAPI學習之Transform組件基本使用

目錄 Transform組件 訪問與獲取 Transform的位置和旋轉信息 Transform局部坐標和旋轉信息的獲取 Transform的縮放與正方向 縮放&#xff08;Scale&#xff09; 正方向 Transform相關的查找方法 銷毀游戲物體 Transform組件 訪問與獲取 現在創建一個容器放置GrisGO物…

操作系統的分類

Linux類系統的組成 Linux操作系統Linux內核Linux應用 Linux內核是什么&#xff1f; Linux系統內核是構成Linux操作系統核心的部分&#xff0c;它是操作系統中最基礎和關鍵的組件&#xff0c;直接與硬件交互并管理計算機系統的底層資源。以下是Linux內核主要特性和功能的概覽…

一起學習大模型 - langchain里的 PromptTemplate詳細介紹

系列文章目錄 一起學習大模型 - 大模型的交互工具prompt簡介與運用 一起學習大模型 - langchain里的PromptTemplate詳細介紹 一起學習大模型 - langchain里PromptTemplate錯誤排查總結 文章目錄 系列文章目錄前言一、 安裝 LangChain二、 基本用法1. 導入庫并定義模板2. 填充…

API接口通道如何設置?

API接口通道如何設置&#xff1f; 如果分站點的AI接口使用openai&#xff08;站點后臺->系統配置->AI參數配置->AI接口&#xff09;&#xff0c;則需要在超管后臺配置接口通道&#xff0c;其他方式則無需在超管后臺配置接口通道 1、進入超管后臺選擇接口通道&#x…

一鍵批量轉換,高效輕松管理:解鎖不同格式圖片統一處理新體驗,讓圖片管理更高效

在信息爆炸的時代&#xff0c;圖片管理成為了一個不容忽視的問題。我們時常面臨各種格式的圖片文件&#xff0c;不同的格式不僅增加了管理的難度&#xff0c;還可能導致兼容性問題。如何快速高效地管理不同格式的圖片&#xff0c;成為了現代人面臨的一大挑戰。現在&#xff0c;…

網上幫別人開網店賣貨的騙局!

小紅書幫別人開店賣貨的騙局主要涉及到一些不法分子利用小紅書平臺的流量和用戶信任度&#xff0c;通過虛假宣傳、承諾高額利潤等手段&#xff0c;誘騙用戶開店并**所謂的“賺錢機會”。 這些騙局往往以“輕松創業、快速致富”為誘餌&#xff0c;吸引那些對創業充滿熱情但缺乏經…

Redis常用命令——List篇

提到List&#xff0c;我們第一時間想到的就是鏈表。但是在Redis中&#xff0c;List更像是一種雙端隊列&#xff0c;例如C中的deque。它可以快速高效的對頭部和尾部進行插入和刪除操作。本片文章主要對List列表的相關命令進行詳解&#xff0c;希望本篇文章會對你有所幫助。 文章…

MedSegDiff-V2: Diffusion-Based Medical Image Segmentation with Transformer 論文總結

標題&#xff1a;MedSegDiff-V2: Diffusion-Based&#xff08;基于擴散模型&#xff09;Medical Image Segmentation&#xff08;醫學圖像分割&#xff09;with Transformer 論文&#xff08;AAAI&#xff09;&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/28…

【避坑全攻略】如何讓私人的LLM擁有一個嗓子——ChatTTS

OpenAI 發布 GPT4o 之后&#xff0c;使得越來越多的人都開始幻想屬于自己的AI“伴侶”&#xff0c;這最讓人驚艷的就是他們出色的TTS技術。而在此之前&#xff0c;主流的開源TTS有 XTTS 2 和 Bark。而近日&#xff0c;一個名為 ChatTTS 文本轉語音項目爆火出圈&#xff0c;引來…