达达帮翰翰给女生送礼物,翰翰一共准备了 NN 个礼物,其中第 ii 个礼物的重量是 G[i]G[i]。
达达的力气很大,他一次可以搬动重量之和不超过 WW 的任意多个物品。
达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。
输入格式
第一行两个整数,分别代表 WW 和 NN。
以后 NN 行,每行一个正整数表示 G[i]G[i]。
输出格式
仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。
数据范围
1≤N≤461≤N≤46,
1≤W,G[i]≤231−11≤W,G[i]≤231−1输入样例:
20 5 7 5 4 18 1
输出样例:
19
解析:这题目可以看成背包问题,但因为W过大DP会超时,又因为N较小,所以可以用dfs;
用dfs时间复杂度为2的N次方,仍会超时,所以我们可以用双向dfs;
解题思路:
1:从大到小枚举每个物品的重量,先枚举重量大的会使分支较小
2:先dfs前N / 2的每个数的和假设定为s1,
再枚举后N / 2的每个数的和假设定位s2,
所以s1 + s2 <= w -> s2 <= w - s1;
最后用二分找出s2中满足小于w - s1的最大的数;
时间复杂度o(N / 2 + N / 2 * log(N / 2));
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int N = 50, M = 1 << 24; int ans; int w[N]; int n, m; int weight[M], cnt = 1; void dfs1(int u, int s) { if (u == n / 2) { weight[cnt ++ ] = s; return ; } dfs1(u + 1, s);//若不选当前的第u个元素 if (s + (LL)w[u] <= m) dfs1(u + 1, (LL)s + w[u]);//若选择第u个元素 } void dfs2(int u, int s) { if (u == n) { int l = 0, r = cnt - 1; while (l < r)//从dfs1中找出大于等于m - s的最小值中 { int mid = l + r + 1 >> 1; if (weight[mid] <= m - s) l = mid; else r = mid - 1; } if (s + weight[r] <= m) ans = max(ans, s + weight[r]);//更新最大值 return ; } dfs2(u + 1, s);//若不选当前的第u个元素 if ((LL)s + w[u] <= m) dfs2(u + 1, s + w[u]);//若选择第u个元素 } int main() { cin >> m >> n; for (int i = 0; i < n; i ++ ) scanf("%d", &w[i]); sort(w, w + n);//将重量从大到小枚举 reverse(w, w + n); int k = n / 2;//先枚举前n / 2个数 dfs1(0, 0); sort(weight, weight + cnt);//排序去重 cnt = unique(weight, weight + cnt) - weight; dfs2(k, 0); cout << ans << endl; return 0; }