搜索_DFS_双向搜索_CH2401_送礼物

版权声明:本文为博主原创作品, 转载请注明出处! https://blog.csdn.net/solider98/article/details/84196962

点此打开题目页面

思路分析: 

    如果直接搜索所有N个物品对应的解空间树, 那么时间复杂度无法接受.考虑, 将N个物品按照重量非递减排序后, 计算前一半物品所能达到的不超过W的所有重量的集合P, 接下来考察后一半物品能达到的所有不超过W的重量e, 对每一个e在P中二分查找元素v, 使得e + v <= W, 且v最大即可, 特别的, 这个前一半和后一半集合的分界bound点为N / 2 - 2时, 程序能够通过所有样例, 具体如下AC代码所示.

//CH2401_送礼物
#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
using namespace std;
const int MAX = 30, NIL = 0x3f3f3f3f;
int G[MAX], W, N, res;
set<int> ps;//G[1...N / 2]可达到的重量集合
int arr[1 << 23], len, bound;//与arr: set对应的数组, bound:集合划分点 
//计算G[1...bound]可达到的重量,level:当前结点层次,G[1]对应第1层,nowSum:当前结点的重量和 
void calc(int level, int nowSum){
	if(level > bound){
		ps.insert(nowSum); return;
	}
	if(W - nowSum >= G[level]) calc(level + 1, nowSum + G[level]);
	calc(level + 1, nowSum);
} 
//计算G[bound + 1...N]和ps中某元素可达到的最大不超过w的重量, G[bound + 1]对应level = bound + 1 
void solve(int level, int nowSum){
	if(level > N){
		int l = 0, r = len, val = W - nowSum, mid;
		while(mid = l + r + 1 >> 1, l < r) if(arr[mid] <= val) l = mid; else r = mid - 1;
		if(l) nowSum += arr[l]; res = max(res, nowSum); return;
	}	
	if(W - nowSum >= G[level]) solve(level + 1, nowSum + G[level]);
	solve(level + 1, nowSum);
} 
int main(){
	cin >> W >> N; for(int i = 1; i <= N; ++i) cin >> G[i]; 
	sort(G + 1, G + N + 1), bound = N / 2 - 2, calc(1, 0); 
	len = 0; for(set<int>::iterator it = ps.begin(); it != ps.end(); ++it) arr[++len] = *it;
	res = -NIL, solve(bound + 1, 0), cout << res << endl;
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/solider98/article/details/84196962