网易2019实习生招聘编程题集合—牛牛的背包问题(分治+二分查找)

[编程题] 牛牛的背包问题

时间限制:1秒

空间限制:32768K

牛牛准备参加学校组织的春游, 出发前牛牛准备往背包里装入一些零食, 牛牛的背包容量为w。
牛牛家里一共有n袋零食, 第i袋零食体积为v[i]。
牛牛想知道在总体积不超过背包容量的情况下,他一共有多少种零食放法(总体积为0也算一种放法)。

输入描述:
输入包括两行
第一行为两个正整数n和w(1 <= n <= 30, 1 <= w <= 2 * 10^9),表示零食的数量和背包的容量。
第二行n个正整数v[i](0 <= v[i] <= 10^9),表示每袋零食的体积。


输出描述:
输出一个正整数, 表示牛牛一共有多少种零食放法。

输入例子1:
3 10
1 2 4

输出例子1:
8

例子说明1:
三种零食总体积小于10,于是每种零食有放入和不放入两种情况,一共有2*2*2 = 8种情况。
直接搜索30个物品会超时,但是我们可以把他分成两堆物品 15个 +15个,先把前15个搜索出来的每种情况对应使用背包的空间记录下来。记为u1

对于后15个也同样这样做。 记为u2

这样结果等于u1中的每个元素与u2中的每个元素简单的双重循环相加,只要结果不大于背包容量即可,每符合一个结果总数便加一。

可以先给u1和u2排序,然后使用二分查找加速这个过程,要不然还是超时,跟直接搜索30个没区别。

//网易2019实习生招聘编程题集合  [编程题] 牛牛的背包问题 
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
ll n,w;
vector<ll> v1,v2;
vector<ll> u1,u2;

void dfs1(ll pos,ll cap) {
	if(pos==v1.size()) {
		u1.push_back(w-cap);
		return;
	}
	
	dfs1(pos+1,cap);
	if(cap>=v1[pos]) {
		dfs1(pos+1,cap-v1[pos]);
	}
}

void dfs2(ll pos,ll cap) {
	if(pos==v2.size()) {
		u2.push_back(w-cap);
		return;
	}
	
	dfs2(pos+1,cap);
	if(cap>=v2[pos]) {
		dfs2(pos+1,cap-v2[pos]);
	}
}

int getindex(ll rem) {
	int l=0,r=u2.size()-1;
	if(u2[r]<=rem) return r;
	if(u2[l]>rem) return -1;
	
	while(1) {
		int mid=(l+r)/2;
		if(rem<u2[mid]) {
			r=mid;
		}else if(rem>=u2[mid]&&rem<u2[mid+1]) {
			return mid;
		}else {
			l=mid;
		}
	}
}

int main() {
	cin>>n>>w;
	int an=n/2,bn=n-an;
	for(int i=0;i<an;i++) {
		ll c; cin>>c;
		if(c<=w) v1.push_back(c);
	}
	for(int i=0;i<bn;i++) {
		ll c; cin>>c;
		if(c<=w) v2.push_back(c);
	}
	
	dfs1(0,w);
	dfs2(0,w);
	
	sort(u1.begin(),u1.end());
	sort(u2.begin(),u2.end());
	
	//for(int i=0;i<u1.size();i++) cout<<"u1 "<<u1[i]<<endl;
	//for(int i=0;i<u2.size();i++) cout<<"u2 "<<u2[i]<<endl;
	
	ll res=0;
    for(int i=0;i<u1.size();i++) {
    	ll rem=w-u1[i];
    	int index=getindex(rem);
    	res+=(index+1);
	}
	cout<<res<<endl;
}



猜你喜欢

转载自blog.csdn.net/ufo___/article/details/80237606