BZOJ1100 [POI2007]砝码Odw 贪心

这道题挺有趣的,记录一下~

首先题目有一个条件:砝码间有倍数关系。

倍数关系其实是一种特殊的进制,我们把容器的容量按照这种进制进行拆分,然后把同位权的位相加,但不进位。

这样做的目的是将不同的容器分开来考虑,比如两个容器的容量是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;
}



你可能感兴趣的:(BZOJ1100 [POI2007]砝码Odw 贪心)