题目描述
译自 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;
}