原題連接html
該問題像是一個 01 背 包 01揹包 01背包問題, 可是 01 背 包 01揹包 01背包的時間複雜度是 O ( N ∗ W ) O(N*W) O(N∗W),再一看本題的數據範圍 1 ≤ W ≤ 2 31 − 1 1≤W≤2^{31}−1 1≤W≤231−1, 確定會超時
若是暴力搜索呢? 時間複雜度大概是 O ( 2 46 ) O(2^{46}) O(246),也會超時
因此咱們就採用雙向搜索
的策略: 把 46 46 46件物品分紅兩部分, 先搜索出前一部分物品所能組成的重量, 再搜索出後一部分物品所能組成的重量 y y y, 而且在搜索第二部分的同時, 二分答案, 從第一部分物品組成的全部重量中二分出一個最大重量x
, 使其知足 x + y ≤ w x+y ≤ w x+y≤w, 從而更新答案
如何分呢? 原則是讓兩部分搜索的時間複雜度大體相同, 因爲第二部分除了搜索以外會進行二分答案,因此就讓第一部分物品多一些,這裏取 前 24 件 前24件 前24件,第二部分取剩餘的 22 22 22件
優化:
1. 1. 1.每次進行搜索以前, 將重量從大到小排序, 這樣在搜索時會更快的達到邊界
2. 2. 2.搜索完第一部分後, 使用unique
去重, 由於第一部分物品所能組成的重量可能有重複ios
關於
unique
的用法: 參考大佬blogweb
參考Y總視頻講解ide
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int N = 50; int m, n, k, cnt, ans; int g[N], w[1<<24]; // 最多會組成2^24種重量 bool cmp(int a, int b) { return a > b; } void dfs_1(int index, int weight) { if(index == k) { w[cnt++] = weight; return; } if((ll)weight + g[index] <= m) dfs_1(index + 1, weight + g[index]); dfs_1(index + 1, weight); } void dfs_2(int index, int weight) { if(index == n) { int l = 0, r = cnt - 1; while(l < r) { int mid = (l + r + 1) >> 1; if((ll)w[mid] + weight <= m) l = mid; else r = mid-1; } ans = max(ans , weight + w[l]); return; } if((ll) weight + g[index] <= m) dfs_2(index + 1, weight + g[index]); dfs_2(index + 1, weight); } int main() { cin >> m >> n; for(int i=0; i<n; i++) cin >> g[i]; sort(g, g+n, cmp); k = n/2 + 2; dfs_1(0,0); sort(w, w+cnt); cnt = unique(w, w+cnt) - w; dfs_2(k, 0); cout << ans << endl; system("pause"); return 0; }