达达帮翰翰给女生送礼物,翰翰一共准备了 个礼物,其中第 个礼物的重量是 。
达达的力气很大,他一次可以搬动重量之和不超过 的任意多个物品。
达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。
输入格式
第一行两个整数,分别代表 和 。
以后 行,每行一个正整数表示 。
输出格式
仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。
数据范围
,
输入样例:
20 5
7
5
4
18
1
输出样例:
19
思路
就是从中选择一些数相加, 他们的和需要小于, 求能构成的最大数。如果用动态规划背包做的话, 时间复杂度为 , 这里体积为 , 肯定超时。
考虑爆搜, 搜索顺序: u为当前枚举的位置, s为当前得到的和
剪枝优化:
- 先搜分支小的, 故为逆向搜索
- 采用双向DFS搜索, 节省时间
双向DFS搜索是指从起点和终点同时搜索, 交汇时就是最终结果, 相比从起点搜到非常深的位置, 这样会少搜一些。用空间换时间。
该题的话就设定一个weight存前半个数组的所有相加和结果, 先DFS前一半数, 每个数选和不选两种情况, 先搜不选的情况, 然后判断是否会超过w(剪枝)再搜选的情况。 其时间复杂度为 即 , n-k为前半部分搜索的复杂度, k为二分搜索 中最大的 , 也就是最大的 。 这里则设定 , 复杂度为 。 若按之前设定为 , 复杂度为
DEBUG 段错误时可以用 exit(0)
来在某行退出, 若可以则在其之上的代码都没问题
代码
const int N = 47;
typedef long long LL;
int a[N];
int n, w, k;
int weight[1 << 25], cnt = 1;
int ans;
void dfs1(int u, int s)
{
if (u == k)
{
weight[cnt++] = s;
return;
}
dfs1(u + 1, s);
if (a[u] <= w - s)
dfs1(u + 1, s + a[u]);
}
void dfs2(int u, int s)
{
if (u >= n)
{
int l = 0, r = cnt - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (weight[mid] <= w - s)
l = mid;
else
r = mid - 1;
}
// cout << s << " " << weight[l] << endl;
ans = max(ans, weight[l] + s);
return;
}
dfs2(u + 1, s);
if (a[u] <= w - s)
dfs2(u + 1, a[u] + s);
}
int main()
{
cin >> w >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a, a + n, greater<int>());
k = n / 2;
dfs1(0, 0);
sort(weight, weight + cnt);
cnt = unique(weight, weight + cnt) - weight;
dfs2(k, 0);
cout << ans << endl;
return 0;
}