zoukankan      html  css  js  c++  java
  • poj3977(折半枚举+二分查找)

    题目链接:https://vjudge.net/problem/POJ-3977

    题意:给一个大小<=35的集合,找一个非空子集合,使得子集合元素和的绝对值最小,如果有多个这样的集合,找元素个数最少的。

    思路:显然,可以用折半搜索,分别枚举一半,最大是2的18次方,复杂度能够满足。因为集合非空,枚举时考虑只在前一半选和只在后一半选的情况。对于前一半后一半都选的情况,把前一半的结果存下来,排序,枚举后一半的时候在前一半里二分查找最合适的即可。

    思路不难,实现有很多细节,最开始用dfs写得一直wa,改成二进制循环才A。

    AC代码:

    #include<cstdio>
    #include<algorithm>
    #include<map>
    using namespace std;
    
    typedef long long LL;
    typedef pair<LL,int> PLI;
    const LL inf1=1e17;
    const int inf2=0x3f3f3f3f;
    int n,m,cnt,ans2;
    LL a[40],ans1;
    PLI b[140005];
    map<LL,int> mp;
    
    LL absLL(LL x){
        return x>=0?x:-x;
    }
    
    int main(){
        while(scanf("%d",&n),n){
            mp.clear();
            cnt=0;
            ans1=inf1;
            ans2=inf2;
            for(int i=1;i<=n;++i)
                scanf("%lld",&a[i]);
            m=n/2;
            for(int i=1;i<(1<<m);++i){
                LL res1=0;
                int res2=0;
                for(int j=0;j<m;++j)
                    if((i>>j)&1){
                        res1+=a[j+1];
                        ++res2;
                    }
                if(absLL(res1)<ans1){
                    ans1=absLL(res1),ans2=res2;
                }
                else if(absLL(res1)==ans1){
                    ans2=min(ans2,res2);
                }
                if(mp.count(res1)){
                    int tmp=mp[res1];
                    b[tmp].second=min(b[tmp].second,res2);
                }
                else{
                    b[++cnt].first=res1,b[cnt].second=res2;
                    mp[res1]=cnt;
                }
            }
            sort(b+1,b+cnt+1);
            for(int i=1;i<(1<<(n-m));++i){
                LL res1=0;
                int res2=0;
                for(int j=0;j<(n-m);++j)
                    if((i>>j)&1){
                        res1+=a[j+1+m];
                        ++res2;
                    }
                if(absLL(res1)<ans1){
                    ans1=absLL(res1),ans2=res2;
                }
                else if(absLL(res1)==ans1){
                    ans2=min(ans2,res2);
                }
                LL tmp=-res1;
                int l=1,r=cnt,mid;
                while(l<=r){
                    mid=(l+r)>>1;
                    if(b[mid].first<=tmp) l=mid+1;
                    else r=mid-1;
                }
                if(r>0){
                    if(absLL(b[r].first+res1)<ans1){
                        ans1=absLL(b[r].first+res1);
                        ans2=b[r].second+res2;
                    }
                    else if(absLL(b[r].first+res1)==ans1){
                        ans2=min(ans2,b[r].second+res2);
                    }
                }
                if(r+1<=cnt){
                    if(absLL(b[r+1].first+res1)<ans1){
                        ans1=absLL(b[r+1].first+res1);
                        ans2=b[r+1].second+res2;
                    }
                    else if(absLL(b[r+1].first+res1)==ans1){
                        ans2=min(ans2,b[r+1].second+res2);
                    }
                }
            }
            printf("%lld %d
    ",ans1,ans2);
        }
        return 0;
    }
  • 相关阅读:
    生产环境Redis中的热点key如何发现并优化?
    一条update SQL在MySQL中结束生命历程
    主从测试过程中,如何模拟网络抖动?
    RDS-MySQL物理备份文件恢复到自建数据库
    Python从数据库中读取数据,并打印表格展示数据。
    Python简单巡检MySQL
    RDS-MySQL备份文件恢复到自建数据库
    使用Python读取Excel表格
    设计模式六大原则:迪米特法则
    设计模式六大原则:开闭原则
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/11712066.html
Copyright © 2011-2022 走看看