您的位置:首页 > 产品设计 > UI/UE

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

2017-11-19 20:09 441 查看
传送门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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: