【BZOJ4800】[Ceoi2015]Ice Hockey World Championship Meet in the Middle

【BZOJ4800】[Ceoi2015]Ice Hockey World Championship

Description

有n个物品,m块钱,给定每个物品的价格,求买物品的方案数。

Input

第一行两个数n,m代表物品数量及钱数
第二行n个数,代表每个物品的价格
n<=40,m<=10^18

Output

一行一个数表示购买的方案数
(想怎么买就怎么买,当然不买也算一种)

Sample Input

5 1000
100 1500 500 500 1000

Sample Output

8

题解:一开始以为是某种搜索+剪枝,后来发现死活剪不掉啊。

正解是Meet in the Middle,什么是Meet in the Middle呢?先DFS出前20个物品的2^20种情况,再DFS出后20个物品的所有情况,然后将这些情况分别按总价钱排序,拿双指针扫一下就行了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int n,dep,n1,n2;
ll ans;
ll m,sum;
ll v[50],s1[(1<<20)+10],s2[(1<<20)+10];
void dfs(int x)
{
	if(x>dep)
	{
		if(dep==n/2)	s1[++n1]=sum;
		else	s2[++n2]=sum;
		return ;
	}
	if(sum+v[x]<=m)	sum+=v[x],dfs(x+1),sum-=v[x];
	dfs(x+1);
}
bool cmp(ll a,ll b)
{
	return a>b;
}
int main()
{
	scanf("%d%lld",&n,&m);
	int i,j;
	for(i=1;i<=n;i++)	scanf("%lld",&v[i]);
	dep=n/2,dfs(1),dep=n,dfs(n/2+1);
	sort(s1+1,s1+n1+1),sort(s2+1,s2+n2+1);
	for(i=1,j=n2;i<=n1;i++)
	{
		while(s1[i]+s2[j]>m)	j--;
		ans+=j;
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/CQzhangyu/p/7242882.html