BZOJ1100 [POI2007]砝码Odw 贪心

来源:互联网 发布:淘宝商城女装特价冬装 编辑:程序博客网 时间:2024/05/17 07:29

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

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

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

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



0 0