砝码称重2
【题目描述】
有n个砝码,现要称一个质量为m的物体,询问最少需要挑出几个砝码来称,一个砝码最多只能挑一次。
【输入描述】
第一行输入两个整数n和m;
接下来n行,每行输入一个整数表示砝码的重量。
【输出描述】
输出一个整数表示最少需要的砝码数。
【样例输入】
3 10
5
9
1
【样例输出】
2
【数据范围及提示】
1 <= n <= 30,1 <= m <= 231,1 <= 每个砝码的质量 <= 230。
源代码: #include<cstdio> #include<iostream> #include<algorithm> using namespace std; int m,n,L1=1,L2=1,Min=1000000000,i[31]; struct Node { int Sum,Weight; }F1[400000],F2[400000]; //计算好情况数目,别想当然。 int Rule(Node t1,Node t2) { return t1.Weight<t2.Weight; } void DFS1(int t,int S,int W) //t代表当前砝码编号,S代表已使用的砝码数量,W代表已使用的砝码总重。 { if (t>(n>>1)) return; F1[++L1].Sum=S+1; F1[L1].Weight=W+i[t]; DFS1(t+1,S+1,W+i[t]); //取还是不取。 DFS1(t+1,S,W); } void DFS2(int t,int S,int W) //同理于上。 { if (t>n) return; F2[++L2].Sum=S+1; F2[L2].Weight=W+i[t]; DFS2(t+1,S+1,W+i[t]); DFS2(t+1,S,W); } int Find(int t) //排序之后,二分查找。 { int Left=0,Right=L2,Mid=L2>>1; while (Left<=Right) { if (F2[Mid].Weight>t) { Right=Mid-1; Mid=(Left+Right)>>1; } if (F2[Mid].Weight<t) { Left=Mid+1; Mid=(Left+Right)>>1; } if (F2[Mid].Weight==t) return Mid; } return -1; } int main() //然而并没有用到Hash。 { scanf("%d%d",&n,&m); for (int a=1;a<=n;a++) scanf("%d",&i[a]); sort(i+1,i+n+1); DFS1(1,0,0); sort(F1+1,F1+L1+1,Rule); DFS2((n>>1)+1,0,0); sort(F2+1,F2+L2+1,Rule); for (int a=1;a<=L1;a++) { int t=Find(m-F1[a].Weight); if (t!=-1) //符合条件。 Min=min(Min,F1[a].Sum+F2[t].Sum); } printf("%d",Min); return 0; } /* 解题思路: 本质上就是定义一个左根堆和一个右根堆,这样可以节省很多空间。之所以排序,是为了保证二分的正确性,然后进行查找匹配。 */