折半搜索

折半搜索 (meet in the middle)

/*
reference:

translation:

solution:

trigger:

note:
    *
date:
    2019.09.04
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dwn(i,a,b) for(int i=a;i>=b;--i)
template <typename T> inline void rd(T &x){x=0;char c=getchar();int f=0;while(!isdigit(c)){f|=c=='-';c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}x=f?-x:x;}
#define mem(a,b) memset(a,b,sizeof(a))
#define ee(i,x) for(int i=head[x];i;i=e[i].next)

int n,t;
int sum,tmp;
int a[50];

bool found=false;
map<int,bool> mp;

inline void dfs1(int h,int t){
    if(h>t){
        mp[tmp]=1;
        return ;
    }
    dfs1(h+1,t);
    tmp+=a[h];
    dfs1(h+1,t);
    tmp-=a[h];
}

inline void dfs2(int h,int t){
    if(h>t){
        if(mp[sum-tmp])found=1;
        return ;
    }
    dfs2(h+1,t);
    tmp+=a[h];
    dfs2(h+1,t);
    tmp-=a[h];
}

#undef int
int main(){
#define int long long
    rd(n),rd(sum);
    rep(i,1,n)rd(a[i]);
    dfs1(1,n/2);
    dfs2(n/2+1,n);
    printf(found?"YES":"NO");
    return 0;
}
/*
5 67
34 546 5 35 32
*/

CF888E Maximum_Subsequence

/*
reference:

translation:

solution:
    考虑到dfs的效率很低很低而且mod数在1e9的范围,肯定要用一个stl的容器啊(set)
    2的35次方会超时,考虑折半搜索,前后分别枚举,最后二分取最大值即可。
trigger:

note:
    *
date:
    2019.09.04
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dwn(i,a,b) for(int i=a;i>=b;--i)
template <typename T> inline void rd(T &x){x=0;char c=getchar();int f=0;while(!isdigit(c)){f|=c=='-';c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}x=f?-x:x;}
#define mem(a,b) memset(a,b,sizeof(a))
#define ee(i,x) for(int i=head[x];i;i=e[i].next)

const int N = 40;
int a[N],n,mod,ans;
set<int>s,t;

inline void dfs1(int cur,int sum){
    if(cur==n>>1|1){
        sum=sum%mod;
        s.insert(sum);
        return ;
    }
    dfs1(cur+1,sum);
    dfs1(cur+1,sum+a[cur]);
}

inline void dfs2(int cur,int sum){
    if(cur==n+1){
        sum=sum%mod;
        t.insert(sum);
        return ;
    }
    dfs2(cur+1,sum);
    dfs2(cur+1,sum+a[cur]);
}

#undef int
int main(){
#define int long long
    rd(n),rd(mod);
    rep(i,1,n){
        rd(a[i]);
    }
    dfs1(1,0);
    dfs2(n>>1|1,0);
    for(auto it=s.begin();it!=s.end();++it){
        int tmp=*it;
        auto pos=t.lower_bound(mod-tmp);
        if(pos!=t.end()){
            --pos;
            if(*pos+tmp<=mod)
                ans=max(ans,*pos+tmp);
        }
    }
    printf("%lld",ans);
    return 0;
}

法2:two_pointers

/*
4 4
5 2 4 1
*/
//3
/*
3 20
199 41 299
*/
//19

/*
reference:

translation:

solution:
    法2:two_pointer 来找两个区间的在一定值的限定区间的最大值,如本题要求
    在x数组和y数组(要排个序)中各选择一个数的和<=mod-1,并使这个值最大
    很显然,快指针从前往后,慢指针倒序,如果当前值比mod-1大了那么j--,因为i再怎么往后移值都不可能
    <=mod-1,(因为是按照升序排序的)
trigger:

note:
    *注意最大值<=mod-1而不是<=mod,你太瓜啦
date:
    2019.09.04
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dwn(i,a,b) for(int i=a;i>=b;--i)
template <typename T> inline void rd(T &x){x=0;char c=getchar();int f=0;while(!isdigit(c)){f|=c=='-';c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}x=f?-x:x;}
#define mem(a,b) memset(a,b,sizeof(a))
#define ee(i,x) for(int i=head[x];i;i=e[i].next)

const int N = 40;
int a[N],x[N],y[N],n,mod,ans;
int tot1,tot2;
//set<int>s,t;

inline void dfs1(int cur,int sum){
    if(cur==n/2+1){
        sum=sum%mod;
        x[++tot1]=sum;
        //s.insert(sum);
        return ;
    }
    dfs1(cur+1,sum);
    dfs1(cur+1,sum+a[cur]);
}

inline void dfs2(int cur,int sum){
    if(cur==n+1){
        sum=sum%mod;
        y[++tot2]=sum;
        //t.insert(sum);
        return ;
    }
    dfs2(cur+1,sum);
    dfs2(cur+1,sum+a[cur]);
}

#undef int
int main(){
#define int long long
    freopen("cf888e.txt","r",stdin);
    rd(n),rd(mod);
    rep(i,1,n){
        rd(a[i]);
    }
    dfs1(1,0);
    dfs2(n>>1|1,0);
    /*for(auto it=s.begin();it!=s.end();++it){
        int tmp=*it;
        auto pos=t.lower_bound(mod-tmp);
        if(pos!=t.end()){
            --pos;
            if(*pos+tmp<=mod)/////////////////这里应该是<
                ans=max(ans,*pos+tmp);
        }
    }*/
    sort(x+1,x+tot1+1);
    sort(y+1,y+tot2+1);
    /*rep(i,1,tot1)printf("%lld ",x[i]);
    puts("");
    rep(i,1,tot2)printf("%lld ",y[i]);
    puts("");*/
    for(int i=1,j=tot2;i<=tot1;++i){
        if(x[i]+y[j]>=ans && x[i]+y[j]<=mod-1)
            ans=x[i]+y[j];
        while(j && x[i]+y[j]<ans)
            j--;
    }
    printf("%lld",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/sjsjsj-minus-Si/p/11634700.html

时间: 2024-11-05 23:22:34

折半搜索的相关文章

E. Anya and Cubes (CF #297 (Div. 2) 折半搜索)

E. Anya and Cubes time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Anya loves to fold and stick. Today she decided to do just that. Anya has n cubes lying in a line and numbered from 1 to n

算法复习——哈希表+折半搜索(poj2549)

搬讲义~搬讲义~ 折半搜索感觉每次都是打暴力时用的啊2333,主要是用于降次··当复杂度为指数级别时用折半可以减少大量复杂度··其实专门考折半的例题并不多···一般都是中途的一个小优化··· 然后折半搜索常常与哈希表一起使用··尤其是遇到方程类的问题时··· 哈希表就不说了吧···毕竟比较简单···不懂得看下面题的代码就行了,一道折半与哈希表联合运用的经典方程模型题··· Description Given S, a set of integers, find the largest d suc

FZU 2178 礼物分配 (折半搜索+二分)

题目地址:FZU 2178 由于n最大是30,一次全搜的话妥妥的超时,那么可以采用折半搜索.分成相同的两份,对左边的一堆进行预处理,然后再处理右堆,每一次都对左堆进行二分,找最接近的.由于两个人取的不能相差多于1个,所以要对每个个数分开存储.并排序,排序是为了后边的二分. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <

位运算符 优先级 折半搜索

看编程珠玑,深知二分搜索的用处之大,自己写了一遍,竟然出了死循环.代码如下: 1 int bsearch(int *data, int val,int left, int right) 2 { 3 if(left <= right) 4 { 5 int mid = left + (right-left)>>1; 6 if(data[mid]==val) 7 return mid; 8 else if(data[mid]<val) 9 return bsearch(data,val,

【bzoj4800】[Ceoi2015]Ice Hockey World Championship 折半搜索

题目描述 有n个物品,m块钱,给定每个物品的价格,求买物品的方案数. 输入 第一行两个数n,m代表物品数量及钱数 第二行n个数,代表每个物品的价格 n<=40,m<=10^18 输出 一行一个数表示购买的方案数 (想怎么买就怎么买,当然不买也算一种) 样例输入 5 1000 100 1500 500 500 1000 样例输出 8 题解 裸的折半搜索meet-in-the-middle 由于直接爆搜肯定会TLE,考虑把整个序列分成左右两部分,对于每部分求出它所有可以消耗钱数的方案.然后考虑左右

折半搜索+状态压缩【P3067】 [USACO12OPEN]平衡的奶牛群Balanced Cow S…

Description 给n个数,从中任意选出一些数,使这些数能分成和相等的两组. 求有多少种选数的方案. Input 第\(1\)行:一个整数\(N\) 第\(2\)到\(N+1\)行,包含一个整数\(m_i\) Output 一行:平衡的集合的个数. 看到题的一瞬间数据范围? \(N \leq 20?\)状压! 明显直接做过不去,选择折半搜索. 折半搜索的话会有三种情况 一.选择当前位置 二.选择当前位置,给第一组. 三.选择当前位置,给第二组. 然后直接跑折半搜索+状压即可. 存储类似链式

codeforces 880E. Maximum Subsequence(折半搜索+双指针)

E. Maximum Subsequence time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output You are given an array a consisting of n integers, and additionally an integer m. You have to choose some sequence of

CF1221G Graph And Numbers(折半搜索+图论)

答案=总数-无0-无1-无2+无01+无02+无12-无012 直接详细讲无0和无2 无0为 01和11,无2为01和00,显然二者方案数相同,以下考虑无0 考虑折半搜索,后半段搜索,二进制点权0的位置,保证后半段构成的无0边的基础上 可以得出一个S集合,表示集合内的点随意选择,不在集合内的点只能为1 再搜索前半段,同后半段搜索,得出一个集合T里面的点权为0,则T的贡献为满足\(T\subseteq S\)的S的个数,这个子集和一下即可 原文地址:https://www.cnblogs.com/

[折半搜索]三教

题目描述  三教是第三教学楼的简称,也是由虚幻工作室开发的一款恐怖推理游戏,将在 2020 年一月在 Steam 上推出第一个 Demo.Kanade 正控制着人物玩着满是 bug 的偷跑版.在游戏中,一共有 n  个补给点,如果人物到达补给点 ,将会获得体力 ai ,但是如果在这个补给点用一些操作触发 bug,将会获得体力 2^f(ai) ,其中 f(x) 表示  x 的二进制表示中 1的个数,最牛的 bug 莫过于到达补给点之后你可以选择不要提供的体力,也就是操作之后玩家将不会获得任何体力,