D. Fill The Bag
题目链接:http://codeforces.com/contest/1303/problem/D
题意:给你一个大小为m的包,和n个盒子,每个盒子大小为a[i],且a[i]是2的正整数次幂,每个盒子可以拆分成2个大小相等的盒子,问至少经过几次拆分,才能正好装满m的包,如 果不可能则输出-1。
思路:把m化成2进制存入b数组中,由于a[i]是2的正整数次幂,那用c数组统计2的对应次幂分别有多少个,然后遍历m的二进制从低位到高位,如果对应的c数组有,那就c[i]--,否则 的话就看c[1]到c[i-1]是否可以撮成,可以的话就要那些低位撮,不可以的话就找一个高位进行拆分,然后累加拆分次数即可。
#include<stdio.h> #include<iostream> #include<string.h> #include<math.h> #include<string> #include<algorithm> #include<queue> #include<map> typedef long long ll; using namespace std; ll lo(ll x) { ll v=0; while(x) { v++; x/=2; } return v; } ll b[100],c[100]; int main() { int t; scanf("%d",&t); while(t--) { memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); ll m,n; scanf("%lld%lld",&m,&n); ll sum=0; for(int i=1;i<=n;i++) { ll x; scanf("%lld",&x); sum+=x; c[lo(x)]++; } if(sum<m) { printf("-1\n"); continue; } int x=0; while(m) { x++; b[x]=m%2; m/=2; } ll ans=0; for(int i=1;i<=65;i++) { if(b[i]==0) continue; if(c[i]>0) c[i]--; else { ll y=2,z=-1; for(int j=i-1;j>=1;j--)//看是否可以撮成i, { if(c[j]>=y) { c[j]-=y; z=j+1; break; } else y-=c[j]; y*=2; } if(z!=-1)//可以则对应的减掉 { for(int j=z;j<i;j++) c[j]=0; } else { for(int j=i+1;j<=65;j++)//不可以则找高位拆分 { if(c[j]>0) { c[j]--; for(int k=j-1;k>=i;k--) { c[k]++; ans++; } break; } } } } } printf("%lld\n",ans); } }