双向搜索是为了避免在深层子树上浪费时间
有的问题有初态 和 终态
当我们从初态和终态双向搜索时,就相当已经搜索了整个状态空间
来看一个例题吧
达达帮翰翰给女生送礼物,翰翰一共准备了N个礼物,其中第i个礼物的重量是G[i]。
达达的力气很大,他一次可以搬动重量之和不超过W的任意多个物品。
达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。
输入格式
第一行两个整数,分别代表W和N。
以后N行,每行一个正整数表示G[i]。
输出格式
仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。
N <= 46
w <= 2^31 - 1
我们很容易想到背包问题,不过太大导致数组开不了,然后N比较小
如果暴力枚举是2^46肯定超时
不过我们可以采用双向搜索来搞
我们可以统计前一半的所有情况,复杂度是2^23 <=1e7
然后得到的排个序,对后半部分也可以枚举每一种情况,然后在前一半找出最优解可以二分出答案
所以后半部分是2^(N/2)*log(2^N/2)
然后这题还有一个剪枝就是对于枚举的时候超出了w就直接回溯了
还有对于前一半重复的部分去重,也可以减少很多复杂度(去重用unique函数)
然后搜索里必须遵循的从决策数少的开始原则,可以把礼物从大到小排序,这样枚举的时候就使得搜索树深度变小了
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 47; ll G[N],a[1 << 23]; ll sum,w,ans; int tot,n,cnt,up; bool cmp(ll x,ll y){ return x > y; } void Dfs1(int cur){ if(cur == n/2 + 1) { a[++tot] = sum; return; } sum += G[cur]; if(sum <= w) Dfs1(cur + 1); sum -= G[cur]; if(sum <= w) Dfs1(cur + 1); } void Dfs2(int cur,ll s){ if(cur == n + 1){ int l = 1,r = up; ll x = 0; while(l <= r){ int m = (l + r) >> 1; if(s + a[m] <= w){ x = a[m]; l = m + 1; } else r = m - 1; } if(s + x <= w) ans = max(ans,s + x); return; } if(s + G[cur] <= w) Dfs2(cur + 1,s + G[cur]); Dfs2(cur + 1,s); } int main(){ cin >> w >> n; for(int i = 1;i <= n;i++) cin >> G[i]; sort(G+1,G+n+1,cmp); cnt = ans = sum = 0; tot = 0; Dfs1(1); //cout << ": " << tot << endl; sort(a + 1,a + tot + 1); // for(int i = 1;i <= tot;i++) // cout << a[i] << " "; // puts(""); up = unique(a + 1,a + tot + 1) - (a + 1); //cout << up << endl; Dfs2(n/2 + 1,0); cout << ans << endl; return 0; }
不过这个地方还有个优化,就是数学关系吧
由于我们前半部分是N/2 复杂度是2^(N/2), 后半部分也是N/2复杂度是2^(N/2)*(log2^(N/2)),所以总的复杂度是2^(N/2)*(log2^(N/2))
后半部分是2^(N/2)*(log2^(N/2))起决定作用
我们如果让前面多搜两个礼物的话 复杂度变为
2^(N/2 + 2)*(log2^(N/2 - 2)是小于2^(N/2)*(log2^(N/2))的使前后的复杂度均衡了
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 47; ll G[N],a[1 << 23]; ll sum,w,ans; int tot,n,cnt,up; bool cmp(ll x,ll y){ return x > y; } void Dfs1(int cur){ if(cur == n/2 + 3) { a[++tot] = sum; return; } sum += G[cur]; if(sum <= w) Dfs1(cur + 1); sum -= G[cur]; if(sum <= w) Dfs1(cur + 1); } void Dfs2(int cur,ll s){ if(cur == n + 1){ int l = 1,r = up; ll x = 0; while(l <= r){ int m = (l + r) >> 1; if(s + a[m] <= w){ x = a[m]; l = m + 1; } else r = m - 1; } if(s + x <= w) ans = max(ans,s + x); return; } if(s + G[cur] <= w) Dfs2(cur + 1,s + G[cur]); Dfs2(cur + 1,s); } int main(){ cin >> w >> n; for(int i = 1;i <= n;i++) cin >> G[i]; sort(G+1,G+n+1,cmp); cnt = ans = sum = 0; tot = 0; Dfs1(1); //cout << ": " << tot << endl; sort(a + 1,a + tot + 1); // for(int i = 1;i <= tot;i++) // cout << a[i] << " "; // puts(""); up = unique(a + 1,a + tot + 1) - (a + 1); //cout << up << endl; Dfs2(n/2 + 3,0); cout << ans << endl; return 0; }
事实证明确实如此
学到了