ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-07-06 07:31:40  阅读:159  来源: 互联网

标签:折半 方案 P4799 int 选数 CEOI2015 long 枚举 搜索


题意

给定 \(n\) 个正整数,选择其中的一些数,使得这些数的和 \(\leq m\),其中 \(m \leq 10^{18}\)。求总共有多少个不同的选择方案满足要求。如果存在一种方案观看某场比赛,而另一种方案不观看,则认为这两种方案不同。

思路

看到题目中要求的是选数方案,可以考虑用搜索求解,枚举每一个数选与不选,最终的时间复杂度就为 \(O(2^n)\)。但是本题的 \(n \leq 40\),显然朴素的搜索无法通过。于是需要考虑剪枝。

发现题目中的选数没有一定的先后顺序,那么就可以用类似于双向搜索 的思想,从前后同时开始选。事实上,如果仅把原搜索分成前后两个搜索子区间,时间复杂度也就降到了 \(O(2^{\frac{n}{2}})\),可以通过本题。

于是就可以先分别搜索前后两个子区间,同时记录所有的选数方案得到的数字之和。

最后是统计答案,朴素的做法是分别枚举前后两个子区间的选数方案。但是这样做时间复杂度又退化回了 \(O(2^n)\)。显然需要优化。

可以发现,如果在前半区间的方案 \(a\) 和在后半区间的方案 \(b\) 的选数之和满足题意,那么所有在前半区中数字之和比方案 \(a\) 小的方案也满足题意。也就可以对题目中的一个半区按选数之和从小到大排序,再依次枚举另一个半区之间的方案(记当前枚举的方案的数字之和为 \(s_i\)),用二分查找快速找到排序后的半区内第一个数字之和 \(> m-s_i\) 的方案(因为可能存在数字之和相同的方案)。那所有数字之和小于它的方案就可行了。

最后,别忘了开 long long

code:

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int N=22;
int n,cnta,cntb;
ll m,t[N<<1],ans,a[1<<N],b[1<<N];
void dfs(int now,int maxn,ll val,ll s[],int &cnt)
{
	if(now>maxn)
	{
	    s[++cnt]=val;
	    return ;
	}
	dfs(now+1,maxn,val,s,cnt);
	dfs(now+1,maxn,val+t[now],s,cnt);
}
int main()
{
	scanf("%d%lld",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&t[i]);
	dfs(1,n/2,0ll,a,cnta);
	dfs(n/2+1,n,0ll,b,cntb);
	sort(a+1,a+cnta+1);
	for(int i=1;i<=cntb;i++) ans+=upper_bound(a+1,a+cnta+1,m-b[i])-a-1;
	printf("%lld\n",ans);
	return 0;
}

标签:折半,方案,P4799,int,选数,CEOI2015,long,枚举,搜索
来源: https://www.cnblogs.com/NLCAKIOI/p/14975156.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有