AcWing 171.送礼物

DFS双向搜索的经典例题。

对于DFS和BFS来说双向搜索都能有效降低时间复杂度,但缺点就是会多开销空间,也就是用空间换时间。

由于我们的搜索范围是逐步扩大的所以当我们的每个节点的子节点较多时就可以做到如下:

 红色部分是从反向开始搜索,这样我们就可以明显看出我们有效的降低了时间的复杂度。

题目:

 思路:

先通过对前部分进行搜索预处理出来能拿到的重量的所有情况,由于只搜索的一半的物品所以我们的时间复杂度有 2^N 降低到了2^N/2 ,显著降低了时间复杂度,并且进行打表,之后再对后半部分进行搜索时直接进对搜索出来的值在表中进行二分查找到最大的小于 m 的值并且更新一下答案即可。

剪枝:

①:优化搜索顺序

我们需要对后半部分进行二分查找所以时间复杂度为 2^N/2 * logN/2,那么我们可以适当的增加前面的搜索数据减小后面的搜索数据可以均匀一下,这样我们可以对前面的搜索N / 2 + 2 时时间复杂度最为低,而又由于我们增加了前面的搜索数据,这样我们可以将数据从大到小排序,那么我们搜索前面时会优先搜索到大的部分,会减掉更多的大于m的部分枝叶。

②:可行性剪枝

对于大于m的部分枝叶直接减掉不进行搜索。

代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 
 6 using namespace std;
 7 
 8 typedef long long LL;
 9 
10 const int N = 46;
11 
12 int n, m, res;
13 int g[N];
14 int w[1 << 25];
15 int cnt;
16 
17 void dfs1(int u, int sum) 
18 {
19     if (u == n / 2 + 2)
20     {
21         w[cnt ++ ] = sum;
22         return;
23     }
24 
25     dfs1(u + 1, sum);
26     if ((LL)sum + g[u] <= m) dfs1(u + 1, sum + g[u]);
27 }
28 
29 void dfs2(int u, int sum) 
30 {
31     if (u == n)
32     {
33         int l = 0, r = cnt - 1;
34         while (l < r) 
35         {
36             int mid = l + r >> 1;
37             if (w[mid] + (LL)sum <= m) r = mid;
38             else l = mid + 1;
39         }
40         res = max(res, sum + w[l]);
41         return;
42     }
43 
44     dfs2(u + 1, sum);
45     if ((LL)sum + g[u] <= m) dfs2(u + 1, sum + g[u]);
46 }
47 
48 int main()
49 {
50     cin >> m >> n;
51     for (int i = 0; i < n; i ++ ) cin >> g[i];
52 
53     sort(g, g + n);
54     reverse(g, g + n);
55 
56     dfs1(0, 0);
57 
58     sort(w, w + cnt);
59     cnt = unique(w, w + cnt) - w;
60     reverse(w, w + cnt);
61 
62     dfs2(n / 2 + 2, 0);
63 
64     cout << res << endl;
65 
66     return 0;
67 }
送礼物
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值