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

来源:互联网 发布:张学友唱功 知乎 编辑:程序博客网 时间:2024/06/05 18:05

传送门: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;}


阅读全文
1 0