zoukankan      html  css  js  c++  java
  • poj3977

    2017-08-26 11:38:42

    writer:pprp

    已经是第二次写这个题了,但是还是出了很多毛病

    先给出AC代码:

    解题思路:

    之前在培训的时候只是笼统的讲了讲怎么做,进行二分对其中一边进行暴力枚举,对另一边用lower_bound查找算出的相反数

    现在给出详细一点的思路:

    答案可能在左边枚举的部分,也可能在右边枚举的部分,也可能在两边加起来的和中

    所以从这三个方面不能少考虑;

    还有用到了map所以算出来的key是唯一的,所以当算出来两个key相等的时候,应该采用value也就是cnt比较小的那个数

    细节比较多,很容易就wa

    /*
    @prama: poj 3977
    @writer:pprp
    @declare:暴力+二分
    @date:2017/8/26
    */
    
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <map>
    #define ll long long
    #define IOS ios::sync_with_stdio(false),cin.tie(0);
    #define tag cout <<"-------" << endl;
    
    using namespace std;
    int N;
    const int maxn = 40;
    ll subset[maxn];  //改成ll
    
    
    ll ll_abs(ll a)
    {
        return a > 0 ? a:- a;
    }
    
    int main()
    {
          IOS;
        while(cin >> N && N)
        {
            memset(subset,0,sizeof(subset));
            for(int i = 0 ; i < N ; i++)  
                      cin >> subset[i];
    
            //初始化这个pair,用来记录答案
            pair<ll,int> ans(ll_abs(subset[0]),1);
            map<ll,int> mp;// sum -> cnt
    
            //二进制枚举前一半
            for(int i = 0 ; i < (1 << N/2) ; i++)
            {
                ll sum = 0;
                int cnt = 0;
                for(int j = 0 ; j < N/2 ; j++)
                {
                    //二进制枚举,判断第j为是否被选中
                    if((i >> j)&1)
                    {
                        sum += subset[j];
                        cnt++;
                    }
                }
                //**增加一个判断,如果一个都没有被选中
                if(cnt == 0) continue;
    
                //对结果进行操作,找到当前最小的sum
                ans = min(ans, make_pair(ll_abs(sum),cnt));
    
                //将每次枚举的情况都加入map中去
                map<ll,int>::iterator ii = mp.find(sum);
                if(ii != mp.end())//如果能找到,就采用那个比较小的cnt,因为map的key是unique的所以要进行如下判断
                    ii->second = min(ii->second,cnt);
                else //如果没有找到就直接放到map中去
                    mp[sum] = cnt;
            }
    
            //对后一半进行枚举,得到的sum可从map中寻找最接近-sum的值
            for(int i = 0 ; i < (1 << (N - N/2)); i++)
            {
                ll sum = 0;
                int cnt = 0;
                for(int j = 0 ; j < (N - N/2); j++)
                {
                    if((i >> j) & 1)
                    {
                        cnt++;
                        sum += subset[N/2 +j];
                    }
                }
                //**增加一个判断,如果一个都没有被选中
                if(cnt == 0) continue;
                //对结果进行操作,找到当前最小的sum
                ans = min(ans, make_pair(ll_abs(sum),cnt));
    
                //对后半段进行找最接近于-sum的值
                //运用lower_bound查找的是不小于-sum的值,就是比-sum略大的值
                map<ll,int>::iterator it = mp.lower_bound(-sum);
                if(it != mp.end())//如果可以找到就进行比较
                {
                    ans = min(ans, make_pair(ll_abs(it->first + sum),it->second+cnt));
                }
    
               // 有可能是比-sum略小的值,这个也可以用upper_bound来写 ??
                if(it != mp.begin())
                {
                    it--;
                    ans = min(ans, make_pair(ll_abs(it->first + sum),it->second+cnt));
                }
                //经过测试,不可以采用这个方法
                map<ll,int>::iterator tt = mp.upper_bound(-sum);
                {
                    ans = min(ans, make_pair(ll_abs(it->first + sum),it->second+cnt));
                }
            }
    
            cout << ans.first << " " << ans.second << endl;
        }
        return 0;
    }

    遇到的问题:

    1、数据类型的选择,数据范围是10^15 远远超过int类型了,所以一开始没有检查数组类型,导致wa了很多次,

    也浪费了很长时间。

    2、lower_bound和upper_bound的用法还是不是很清楚。

    最后那部分不可以用upper_bound直接将指针进行移动就可以

  • 相关阅读:
    kmp
    RMP
    p次方求和
    河南省之6 Metric Matrice
    表达式求值
    线段树
    办公软件试题
    河南省之6 遥控器
    三个水杯
    JLink + USBTO232 MINI作品
  • 原文地址:https://www.cnblogs.com/pprp/p/7434919.html
Copyright © 2011-2022 走看看