poj 3977 Subset 枚举+二分

首先分成一半2^17和2^18,并且把其中一半变成相反数,然后枚举一半二分查找另一半,在找到的位置前后也找找。

这里用到了二级排序,有很多细节要处理,不多说了。

巨坑的一个地方就是,不能用系统的abs,要自己手写,简直坑死。。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
typedef long long ll;
struct node
{
    int num;
    ll val;
    bool operator <(const node &x) const
    {
        if(val==x.val) return num<x.num;
        return val<x.val;
    }
}a[300000],b[300000];
ll s[100];
int n1,n;
ll ansx;
int ansk;
int top1,top2;
void dfs1(int now,ll sum,int num)
{
    if(now>n1)
    {
        if(num){
        a[top1].val=sum;
        a[top1++].num=num;
        }
        return;
    }
    dfs1(now+1,sum+s[now],num+1);
    dfs1(now+1,sum,num);
}
void dfs2(int now,ll sum,int num)
{
    if(now>n)
    {
        if(num){
        b[top2].val=-sum;
        b[top2++].num=num;
        }
        return;
    }
    dfs2(now+1,sum+s[now],num+1);
    dfs2(now+1,sum,num);
}
ll myabs(ll a)
{
    return a<0?-a:a;
}
int myabs(int a)
{
    return a<0?-a:a;
}
void cal(int p)
{
    node cq;
    cq.val=a[p].val;
    cq.num=0;
    int tmp=lower_bound(b,b+top2,cq)-b;
    ll tzf=myabs(a[p].val-b[tmp].val);
    if(tzf<ansx)
    {
        ansx=tzf;
        ansk=a[p].num+b[tmp].num;
    }
    else if(tzf==ansx)
    {
        ansk=min(ansk,a[p].num+b[tmp].num);
    }

    int fuck=tmp-1;
    if(fuck<0) return;
    cq.val=b[fuck].val;
    cq.num=0;
    tmp=lower_bound(b,b+top2,cq)-b;
    tmp++;

        if(tmp-1>=0)
        {
            tzf=myabs(a[p].val-b[tmp-1].val);
            if(tzf<ansx)
            {
                ansx=tzf;
                ansk=a[p].num+b[tmp-1].num;
            }
            else if(tzf==ansx)
            {
                ansk=min(ansk,a[p].num+b[tmp-1].num);
            }
        }
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        int flag=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%I64d",&s[i]);
            if(s[i]==0) flag=1;
        }
        if(flag)
        {
            printf("0 1\n");
            continue;
        }
        top1=top2=0;
        ansx=0x7fffffffffffffffll;
        ansk=0x7ffffff;
        n1=n/2;
        dfs1(1,0,0);

        dfs2(n/2+1,0,0);

        sort(a,a+top1);
        sort(b,b+top2);

        for(int i=0;i<top1;i++)
        {
            if(myabs(a[i].val)<ansx)
            {
                ansx=myabs(a[i].val);
                ansk=a[i].num;
            }
            else if(myabs(a[i].val)==ansx)
            {
                ansk=min(ansk,a[i].num);
            }
        }
        for(int i=0;i<top2;i++)
        {
            if(myabs(b[i].val)<ansx)
            {
                ansx=myabs(b[i].val);
                ansk=b[i].num;
            }
            else if(myabs(b[i].val)==ansx)
            {
                ansk=min(ansk,b[i].num);
            }
        }
        for(int i=0;i<top1;i++)
        {
            cal(i);
        }
        printf("%I64d %d\n",ansx,ansk);
    }
    return 0;
}

poj 3977 Subset 枚举+二分

时间: 2024-10-14 09:18:24

poj 3977 Subset 枚举+二分的相关文章

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

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

POJ 3977Subset(枚举+二分)

Subset Time Limit: 30000MS   Memory Limit: 65536K Total Submissions: 1562   Accepted: 261 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

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 2549 折半枚举+二分

三重循环肯定TLE,所以采用“折半枚举”的方法+二分查找来提高速度,不同的是需要保存两个下标用来判定是否有重复元素. 1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 using namespace std; 6 7 const int N = 1000; 8 int a[N]; 9 int n, cnt; 10 11 struct

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

原题 给定N个整数组成的数列(N<=35),从中选出一个子集,使得这个子集的所有元素的值的和的绝对值最小,如果有多组数据满足的话,选择子集元素最少的那个. n<=35,所以双向dfs的O(2^(n/2))可以直接解决问题.因为会爆空间,所以枚举前一半的二进制状态来完成dfs,并用map记录每个状态所用的个数,然后枚举后一半的状态在map中找第一个大于等于他的和第一个小于他的,比较这两个答案. 注:long long 没有自带的abs,并且在define里要多打括号,因为优先度-- #inclu

poj 1873 凸包+枚举

The Fortified Forest Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 6198   Accepted: 1744 Description Once upon a time, in a faraway land, there lived a king. This king owned a small collection of rare and valuable trees, which had been

POJ 2112 Optimal Milking 二分答案+最大流

首先二分最长的边,然后删去所有比当前枚举的值长的边,算最大流,看是否能满足所有的牛都能找到挤奶的地方 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include

POJ 3670 Eating Together 二分单调队列解法O(nlgn)和O(n)算法

本题就是一题LIS(最长递增子序列)的问题.本题要求求最长递增子序列和最长递减子序列. dp的解法是O(n*n),这个应该大家都知道,不过本题应该超时了. 因为有O(nlgn)的解法. 但是由于本题的数据特殊性,故此本题可以利用这个特殊性加速到O(n)的解法,其中的底层思想是counting sort分段的思想.就是如果你不会counting sort的话,就很难想出这种优化的算法了. O(nlgn)的单调队列解法,利用二分加速是有代表性的,无数据特殊的时候也可以使用,故此这里先给出这个算法代码

hdu4430之枚举+二分

Yukari's Birthday Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2549    Accepted Submission(s): 522 Problem Description Today is Yukari's n-th birthday. Ran and Chen hold a celebration party