wikioi-天梯-提高一等-哈希表-2144:砝码称重2

有n个砝码,现在要称一个质量为m的物体,请问最少需要挑出几个砝码来称?

注意一个砝码最多只能挑一次

第一行两个整数n和m,接下来n行每行一个整数表示每个砝码的重量。

输出选择的砝码的总数k,你的程序必须使得k尽量的小。

3 10
5
9
1

2

1<=n<=30,1<=m<=2^31,1<=每个砝码的质量<=2^30

类型:其他  难度:2

题意:给出n个数,要求从中挑出k个数,使它们的和为m,求最小的k

分析:一个简单的思路:

1、就是把n个数分成两份,分别求每份的数所有可能取的和,用cnt1和cnt2两个哈希表记录,其中cnt1[i]=j表示最少需要凑出和为i,至少需要j个数。

遍历方法可以用二进制表示每个数是否取,比如有5个数,那么遍历1 - (2^5-1),每个数二进制为1,则将这个数累加,将累加的和作为哈希表的key,选取的个数作为哈希表的value,若比之前的小或是新出现的则更新哈希表对应项

2、对于两个哈希表cnt1和cnt2,遍历其中一个哈希表,设当前遍历的项为cnt1[i]=j,那么找cnt2[m-i]=k是否存在,若存在,那么j+k即为总共需要取的个数,记录最小值即为所求

3、最后分别看cnt1[m]和cnt2[m]是否存在,就是单独取两部分的和就能构成结果,更新结果为最小值

用stl map实现哈希的功能,比自己写哈希稍微慢了点,不过够用了。

本来尝试用stl hash_map实现,但是hash_map不是ansi c的标准,我用的编译器要加using namespace __gnu_cxx,并且wikioi的编译器不认,就没有用。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<algorithm>
#include<vector>
using namespace std;

map<long long,int> cnt1,cnt2;

int main()
{
    int n;
    long long m;
    vector<int> da;
    scanf("%d%lld",&n,&m);
    
    for(int i=0; i<n; i++)
    {
        int tmp;
        scanf("%d",&tmp);
        da.push_back(tmp);
    }
    sort(da.begin(),da.end());
    
    int l1 = da.size()/2;
    int l2 = da.size() - l1;
    
    for(int i=1; i<1<<l1; i++)
    {
        long long sum=0;
        int tc = 0;
        for(int j=i,k=0; j; j>>=1,k++)
            if(j&1)
            {
                sum += (long long)da[k];
                tc++;
            }
        
        int now = cnt1[sum];
        if(!now || tc<now) cnt1[sum] = tc;
    }
    for(int i=1; i<1<<l2; i++)
    {
        long long sum=0;
        int tc = 0;
        for(int j=i,k=l1; j; j>>=1,k++)
            if(j&1)
            {
                sum += (long long)da[k];
                tc++;
            }
        
        int now = cnt2[sum];
        if(!now || tc<now) cnt2[sum] = tc;
    }
    
    int ans = 50;
    
    map<long long,int>::iterator it;
    for(it=cnt1.begin(); it!= cnt1.end(); it++)
    {
        long long left = m-it->first;
        int tmp = it->second;
        
        if(left > 0)
        {
            int tmp2 = cnt2[left];
            if(tmp2)
            {
                tmp += tmp2;
                ans = min(ans,tmp);
            }
        }
    }
    int r1 = cnt1[m];
    int r2 = cnt2[m];
    if(r1) ans = min(ans,r1);
    if(r2) ans = min(ans,r2);
    printf("%d\n",ans);
}


你可能感兴趣的:(哈希表,WIKIOI,天梯)