搜索之雙向搜索


雙向搜索是為了避免在深層子樹上浪費時間

有的問題有初態 和 終態

當我們從初態和終態雙向搜索時,就相當已經搜索了整個狀態空間

 

來看一個例題吧

達達幫翰翰給女生送禮物,翰翰一共准備了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;
} 

  事實證明確實如此

學到了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM