POJ3977:Subset——题解(三分+折半搜索)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ3977:Subset——题解(三分+折半搜索)相关的知识,希望对你有一定的参考价值。

http://poj.org/problem?id=3977

题目大意:有一堆数,取出一些数,记他们和的绝对值为w,取的个数为n,求在w最小的情况下,n最小,并输出w,n。

————————————————————

两天时间,终于搞下。

这题显然我们唯一能做到的只有暴力,但是2^35显然不可取……

但是显然我们折半搜索的话复杂度只有2^18左右所以没问题。

将数分成两堆,每一堆暴力求出所有情况并记录。

然后枚举第一堆,三分第二堆求解即可。

(为什么三分呢?因为绝对值啊,一定最优解是在函数的最低点,所以是单峰函数)

……思路挺简单是不是,但是注意以下几点:

1.n不为零,这点需要特判。

2.三分很容易写跪,具体怎么做看我代码。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll INF=8223372036854775807;
inline ll abss(ll a){
    if(a<0)return -a;
    return a;
}
struct num{
    ll w;
    ll n;
}mp1[300001],mp2[300001];
ll a[36];
int cnt1=0,cnt2=0;;
bool in[36];
void dfs(int n,int k,bool t){
    memset(in,0,sizeof(in));
    if(!t){
    mp1[++cnt1].w=mp1[cnt1].n=0;
    }
    for(int res=1;res<=((1<<k)-1);res++){
    int cnt=0;
    int lz=res;
    while(lz){
        in[++cnt]=lz-lz/2*2;
        lz/=2;
    }
    ll sum=0,num=0;
    for(int i=1;i<=cnt;i++){
        int j=i;
        if(t)j+=n/2;
        if(in[i]){
        sum+=a[j];
        num++;
        }
    }
    if(!t){
        mp1[++cnt1].w=sum;
        mp1[cnt1].n=num;
    }else{
        mp2[++cnt2].w=sum;
        mp2[cnt2].n=num;
    }
    }
    return;
}
bool cmp(num c,num d){
    if(c.w<d.w)return 1;
    if(c.w>d.w)return 0;
    if(c.n<d.n)return 1;
    return 0;
}
ll ans,cnt;
void sanfen(int l,int r,num k){
    if(r-l<=2){
    ll t1=abss(k.w+mp2[l].w);
    ll t2=abss(k.w+mp2[r].w);
    ll t3=abss(k.w+mp2[(l+r)/2].w);
    ll c1=mp2[l].n+k.n;
    ll c2=mp2[r].n+k.n;
    ll c3=mp2[(l+r)/2].n+k.n;
    ll t,c;
    if(t1>t2||(t1==t2&&c1>c2)){
        t=t2;c=c2;
    }else{
        t=t1;c=c1;
    }
    if(t>t3||(t==t3&&c>c3)){
        t=t3;c=c3;
    }
    if(ans>t||(ans==t&&cnt>c)){
        ans=t;cnt=c;
    }
    return;
    }
    int mid1=(r+2*l)/3;
    int mid2=(l+2*r)/3;
    ll t1=abss(k.w+mp2[mid1].w);
    ll t2=abss(k.w+mp2[mid2].w);
    if(t1<t2||(t1==t2&&mp2[mid1].n+k.n<mp2[mid2].n+k.n)){
    sanfen(l,mid2-1,k);
    }else{
    sanfen(mid1+1,r,k);
    }
    return;
}
int main(){
    int n;
    while(scanf("%d",&n)!=EOF&&n){
    cnt1=0,cnt2=0;
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    dfs(n,n/2,0);
    dfs(n,n-n/2,1);
    sort(mp2+1,mp2+cnt2+1,cmp);
    //for(int i=1;i<=cnt1;i++)printf("1 %lld %lld\n",mp1[i].w,mp1[i].n);
    //for(int i=1;i<=cnt2;i++)printf("2 %lld %lld\n",mp2[i].w,mp2[i].n);
    ans=INF;cnt=INF;
    for(int i=1;i<=cnt1;i++){
        sanfen(1,cnt2,mp1[i]);
        if(i>1){
        if(ans>abss(mp1[i].w)||(ans==abss(mp1[i].w)&&cnt>mp1[i].n)){
            ans=abss(mp1[i].w);cnt=mp1[i].n;
        }
        }
    }
    printf("%lld %lld\n",ans,cnt);
    }
    return 0;
}

 

以上是关于POJ3977:Subset——题解(三分+折半搜索)的主要内容,如果未能解决你的问题,请参考以下文章

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

POJ3977 Subset 折半枚举

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

POJ3977 Subset

POJ3977 Subset

POJ 3977 Subset

(c)2006-2024 SYSTEM All Rights Reserved IT常识