砝码称重 2
时间限制: 1 s
空间限制: 16000 KB
题目等级 : 钻石 Diamond
题解
题目描述 Description
有n个砝码,现在要称一个质量为m的物体,请问最少需要挑出几个砝码来称?
注意一个砝码最多只能挑一次
输入描述 Input Description
第一行两个整数n和m,接下来n行每行一个整数表示每个砝码的重量。
输出描述 Output Description
输出选择的砝码的总数k,你的程序必须使得k尽量的小。
样例输入 Sample Input
3 10
5
9
1
样例输出 Sample Output
2
数据范围及提示 Data Size & Hint
1<=n<=30,1<=m<=2^31,1<=每个砝码的质量<=2^30
写在前面:我就是不会写C++自带的队列,哈希和堆……
————————————————————————————————————————————————————————
写在前面:如果我们用暴力的话回过60%的点,但是之后就死活AC不了,无奈看了看题解,但发现每一个能看懂代码的(想法能理解,但是一用hash就看不懂了),最后怒打排序+二分过的,排序后分成两部分,逐个dfs求和,每个部分最多2^15-1个,所以数组还是存的下的,然后逐个求差并在另一个数组内查找就可以了
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[31],n,m,l1,l2;
struct os
{
int num,w;//num代表所用砝码数,w指称出的质量
}hash1[400000],hash2[400000];
int comp(os x,os y)
{
return x.w<y.w;
}
void dfs1(int x,int y,int z)
{
if (x>n/2) return;
hash1[++l1].num=y+1;
hash1[l1].w=z+a[x];
dfs1(x+1,y+1,z+a[x]);
dfs1(x+1,y,z);
}
void dfs2(int x,int y,int z)
{
if (x>n) return;
hash2[++l2].num=y+1;
hash2[l2].w=z+a[x];
dfs2(x+1,y+1,z+a[x]);
dfs2(x+1,y,z);
}
int find(int x)
{
int l=0,r=l2,mid=(l2+1)/2;
while (l<=r)
{
if (hash2[mid].w>x) {r=mid-1;mid=(l+r)/2;}
if (hash2[mid].w<x) {l=mid+1;mid=(l+r)/2;}
if (hash2[mid].w==x) return mid;
}
return -1;
}
main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
l1++;
l2++;
dfs1(1,0,0);
sort(hash1+1,hash1+l1+1,comp);
dfs2(n/2+1,0,0);
sort(hash2+1,hash2+l2+1,comp);
int minn=100;
for (int i=1;i<=l1;i++)
{
int flag=find(m-hash1[i].w);
if (flag!=-1)
minn=min(minn,hash1[i].num+hash2[flag].num);
}
printf("%d",minn);
}