codeforces 888E Maximum Subsequence (折半枚举 双向搜索)

传送门codeforces 888E



题目大意

从大小为 n 的数组 a 中挑选出任意个元素,使得元素之和模 m 最大。



前置技能

1.折半枚举。举个栗子,如果让你在 n 个数中找出 4 个数满足它们的和等于 sum。如果 n 很大的话四层循环会超时。这时就可以用折半枚举,也就是计算每两个数的和,然后从 2 个数之和中挑出 2 个满足题意的即可,只需要注意下标互不相同。


2.set集合的二分查找函数 upper_bound(),自然也有 lower_bound()。用法是 it = st.upper_bound(x);  x为要查找的值,函数返回值为 set 的迭代器。upper_bound() 查找的是元素的最后一个可安插位置,也就是“ 元素值 > 查找值 ”的第一个元素的位置。



思路

对于每个元素来说,可取可不取,最多 35 个元素,共 2^35 种可能,直接枚举会超时。所以可以用折半枚举,每次计算一半的数组可以形成的值,并存入两个集合。然后枚一个集合的元素值,在另一个集合中二分搜索满足两者和 <m 的最大值,同时更新最大值。


为什么用集合是可以的呢?因为如果有两个相同的元素,说明取两者中的任意一个都可以。



代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;

int n,m,a[40];
set<int> st1,st2;
set<int>:: iterator it,jt;

void dfs(int sum,int l,int r)
{ //用深搜进行搜索 
	if(l==r)
	{ //当搜索完毕将产生的值插入集合并退出 
		if(r==n) st1.insert(sum);		
		else st2.insert(sum);
		return;
	}	
	dfs((sum+a[l])%m,l+1,r); //取当前元素 
	dfs(sum,l+1,r); //不取当前元素 
}

int main()
{
	int i,j,mx;
	while(~scanf("%d%d",&n,&m))
	{
		for(i=0;i<n;i++) scanf("%d",&a[i]);
		dfs(0,0,n/2); //枚举前半个数组可以产生的值 
		dfs(0,n/2,n); //枚举后半个数组可以产生的值
		st1.insert(0); //插入0是为了防止某个集合中的元素已是最大的情况 
		st2.insert(0);
		mx=0;
		for(it=st1.begin();it!=st1.end();it++)
		{ //枚举一个集合的值 
			jt=st2.upper_bound(m-1-*it); //在另一个集合二分搜索 
			jt--;
			if(*it+*jt>mx) mx=*it+*jt; //更新最大值 
		}
		printf("%d\n",mx);
	}
	return 0;
}


  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值