zoukankan      html  css  js  c++  java
  • 搜索之双向搜索

    双向搜索是为了避免在深层子树上浪费时间

    有的问题有初态 和 终态

    当我们从初态和终态双向搜索时,就相当已经搜索了整个状态空间

    来看一个例题吧

    达达帮翰翰给女生送礼物,翰翰一共准备了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;
    } 
    

      事实证明确实如此

    学到了

  • 相关阅读:
    十七:CSS之CSS继承和层叠
    十六:CSS之CSS选择器之后代选择器、伪类选择器
    十五:CSS之CSS选择器之群组选择器、全局选择器
    十四:CSS之CSS选择器之标签选择器、类选择器、ID选择器
    Android开发技巧——ViewPager加View情况封装PagerAdapter的实现类
    JAVA知识笔记
    机器学习笔记
    设计模式学习
    Android小知识汇总
    判断GPS是否开启&转到设置GPS界面
  • 原文地址:https://www.cnblogs.com/mch5201314/p/12076549.html
Copyright © 2011-2022 走看看