原题连接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; }