[poj] 3977 Subset || 折半搜索MITM

原题

给定N个整数组成的数列(N<=35),从中选出一个子集,使得这个子集的所有元素的值的和的绝对值最小,如果有多组数据满足的话,选择子集元素最少的那个。



n<=35,所以双向dfs的O(2^(n/2))可以直接解决问题。因为会爆空间,所以枚举前一半的二进制状态来完成dfs,并用map记录每个状态所用的个数,然后枚举后一半的状态在map中找第一个大于等于他的和第一个小于他的,比较这两个答案。

注:long long 没有自带的abs,并且在define里要多打括号,因为优先度……

#include<cstdio>
#include<map>
#define abs(x) ((x)>0?(x):-(x))
typedef long long ll;
using namespace std;
ll n,a[40],ans,sum;
int cnt;
map <ll,int> mp;
map <ll,int> :: iterator qwq;

ll read()
{
    ll ans=0,fu=1;
    char j=getchar();
    for (;(j<'0' || j>'9') && j!='-';j=getchar()) ;
    if (j=='-') j=getchar(),fu=-1;
    for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0';
    return ans*fu;
}

int main()
{
    while (~scanf("%lld",&n) && n)
    {
    mp.clear();
    ans=0;
    cnt=0;
    for (int i=1;i<=n;i++)
        a[i]=read();
    ans=abs(a[1]);
    cnt=1;
    for (int i=1,j,now,count;i<(1<<(n/2));i++)
    {
        sum=0;
        j=i;
        now=0;
        count=0;
        while (j)
        {
        if (j&1)
            sum+=a[now+1],count++;
        j>>=1;
        now++;
        }
        if (abs(sum)<ans)
        {
        ans=abs(sum);
        cnt=count;
        }
        else if (abs(sum)==ans) cnt=min(cnt,count);
        if (mp[sum])
        mp[sum]=min(mp[sum],count);
        else
        mp[sum]=count;
    }
    for (int i=1,j,now,count;i<(1<<(n-n/2));i++)
    {
        j=i;
        sum=0;
        count=0;
        now=0;
        while (j)
        {
        if (j&1)
            sum+=a[now+n/2+1],count++;
        j>>=1;
        now++;
        }
        if (abs(sum)<ans)
        {
        ans=abs(sum);
        cnt=count;
        }
        else if (abs(sum)==ans) cnt=min(cnt,count);
        qwq=mp.lower_bound(-sum);
        ll nw;
        if (qwq!=mp.end())
        {
        nw=sum+qwq->first;
        nw=abs(nw);
        if (nw<ans)
        {
            ans=nw;
            cnt=qwq->second+count;
        }
        else if (nw==ans) cnt=min(cnt,qwq->second+count);
        }
        if (qwq!=mp.begin())
        {
        qwq--;
        nw=sum+qwq->first;
        nw=abs(nw);
        if (nw<ans)
        {
            ans=nw;
            cnt=qwq->second+count;
        }
        else if (nw==ans) cnt=min(cnt,qwq->second+count);
        }
    }
    printf("%lld %d\n",ans,cnt);
    }
    return 0;
}
时间: 2024-08-12 15:16:53

[poj] 3977 Subset || 折半搜索MITM的相关文章

poj 3977 Subset 枚举+二分

首先分成一半2^17和2^18,并且把其中一半变成相反数,然后枚举一半二分查找另一半,在找到的位置前后也找找. 这里用到了二级排序,有很多细节要处理,不多说了. 巨坑的一个地方就是,不能用系统的abs,要自己手写,简直坑死.. #include<cstdio> #include<algorithm> #include<iostream> #include<map> using namespace std; typedef long long ll; stru

poj 3977 Subset

Subset Time Limit: 30000MS   Memory Limit: 65536K Total Submissions: 3662   Accepted: 673 Description Given a list of N integers with absolute values no larger than 1015, find a non empty subset of these numbers which minimizes the absolute value of

POJ - 3977 Subset(二分+折半枚举)

题意:有一个N(N <= 35)个数的集合,每个数的绝对值小于等于1015,找一个非空子集,使该子集中所有元素的和的绝对值最小,若有多个,则输出个数最小的那个. 分析: 1.将集合中的元素分成两半,分别二进制枚举子集并记录子集所对应的和以及元素个数. 2.枚举其中一半,二分查找另一半,不断取最小值. #pragma comment(linker, "/STACK:102400000, 102400000") #include<cstdio> #include<c

POJ 2386 Lake Counting 搜索题解

简单的深度搜索就可以了,看见有人说什么使用并查集,那简直是大算法小用了. 因为可以深搜而不用回溯,故此效率就是O(N*M)了. 技巧就是增加一个标志P,每次搜索到池塘,即有W字母,那么就认为搜索到一个池塘了,P值为真. 搜索过的池塘不要重复搜索,故此,每次走过的池塘都改成其他字母,如'@',或者'#',随便一个都可以. 然后8个方向搜索. #include <stdio.h> #include <vector> #include <string.h> #include

poj 1699 Best Sequence (搜索技巧 剪枝 dfs)

题目链接 题意:给出几个基因片段,要求你将它们排列成一个最短的序列,序列中使用了所有的基因片段,而且不能翻转基因. 分析:先计算出add数组,再dfs枚举. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <cstdio> 6 #include <vector> 7 #include <al

POJ 2196 Computer(搜索-深度优先搜索)

Computer Problem Description A school bought the first computer some time ago(so this computer's id is 1). During the recent years the school bought N-1 new computers. Each new computer was connected to one of settled earlier. Managers of school are

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 <