这道题挺有趣的,记录一下~
首先题目有一个条件:砝码间有倍数关系。
倍数关系其实是一种特殊的进制,我们把容器的容量按照这种进制进行拆分,然后把同位权的位相加,但不进位。
这样做的目的是将不同的容器分开来考虑,比如两个容器的容量是2、2,而三个砝码质量分别为1、1、2,那么先放了两个1以后,2是没有地方放的。如果将质量直接相加,就会导致2可以放。直观来看,就是先将每一个容器的零头拿来,放砝码,然后再把大的空间拆开,保证了不会用几个容器的零头来装一个大砝码(这是不符合题意的)。
然后因为题目求的是砝码个数最多,显然小的砝码会更加合适,因为如果一个最优解中用了一个大砝码,却没有用一个小砝码,把大砝码换掉,答案不会变差。
这样我们就考虑将砝码按照质量从小到大的顺序依次强制放入。
把我们得到的每一个位权上的数量拿来,得到一个砝码的时候,从小到大找能放下它的位权,如果能放下,就把这个位权减掉1,拆散成为这个砝码对应的位权,然后再放。这样做相当于把大的空间拆开,放入小砝码。因为小砝码一定比大砝码好,这样做也保证了答案的最优。
感谢Someday神犇让我弄懂了这道题目。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int MAXN=100010;
int n,m,w[MAXN],a[MAXN],p[MAXN],base[MAXN],b[MAXN][40],B[40],tot,ans;
void bit(int sub)
{
int x=w[sub];
for(int i=tot;i>=1;i--)
{
b[sub][i]=x/base[i];
x-=b[sub][i]*base[i];
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
sort(a+1,a+m+1);
for(int i=1;i<=m;i++)
if(a[i]!=a[i-1]) base[++tot]=a[i],p[i]=tot;
else p[i]=tot;
for(int i=1;i<=n;i++) bit(i);
for(int i=1;i<=tot;i++)
for(int j=1;j<=n;j++)
B[i]+=b[j][i];
for(int i=1;i<=m;i++)
{
for(int j=p[i];j<=tot;j++)
{
if(B[j]==0) continue;
B[j]--,B[p[i]]+=base[j]/base[p[i]];
B[p[i]]--,ans++;
break;
}
}
printf("%d\n",ans);
return 0;
}