P4799 [CEOI2015 Day2]世界冰球锦标赛(折半搜索)

题目描述

译自 CEOI2015 Day2 T1「Ice Hockey World Championship

今年的世界冰球锦标赛在捷克举行。Bobek 已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。

给出 Bobek 的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。

输入格式

第一行,两个正整数 N和 M(1≤N≤40,1≤M≤10^18),表示比赛的个数和 Bobek 那家徒四壁的财产。

第二行,N 个以空格分隔的正整数,均不超过 10^16,代表每场比赛门票的价格。

输出格式

输出一行,表示方案的个数。由于 NN 十分大,注意:答案 ≤2^40。

输入输出样例

输入 #1复制

5 1000
100 1500 500 500 1000

输出 #1复制

8

注意到数据范围,背包肯定不可,一开始还想过离散化但是仔细一想就知道离散化并不能成比例缩小。因此只能搜索。但2的40次方肯定会t,此时要么剪枝,要么就采用折半搜索。

对于本题来说我们可以把序列分成前后两部分分别搜,搜出来的答案分别存入两个vector,然后对其中一个vector v2排序,在遍历另一个vector v的时候,在v2里二分查找第一个大于m - v1[i]的位置更新答案即可。这样也能保证所选方案各不相同。

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
int n, n1, n2;
long long m, a1[45], a2[45];
vector<long long> v1, v2;
void dfs1(int x, long long tot)
{
	if(x == n1 + 1)
	{
		if(tot <= m)
			v1.push_back(tot);
		return;
	}
	dfs1(x + 1, tot);
	dfs1(x + 1, tot + a1[x]);
}
void dfs2(int x, long long tot)
{
	if(x == n2 + 1)
	{
		if(tot <= m)
			v2.push_back(tot);
		return;
	}
	dfs2(x + 1, tot);
	dfs2(x + 1, tot + a2[x]);
}
int main()
{
	freopen("data.txt", "r", stdin);
	cin >> n >> m;
	n1 = (n + 1) >> 1;
	n2 = n - n1;
	for(int i = 1; i <= n1; i++)
		cin >> a1[i];
	for(int i = 1; i <= n2; i++)
		cin >> a2[i];
	dfs1(1, 0);
	dfs2(1, 0);
	long long ans = 0;
	sort(v2.begin(), v2.end());
	for(int i = 0; i < v1.size(); i++)
	{
		int pos = upper_bound(v2.begin(), v2.end(), m - v1[i]) - v2.begin();
		ans += pos;
	}
	cout << ans;
	return 0;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/14435389.html