Allowance

Allowance

有n种数字,第i种数字值为\(v_i\),有\(b_i\)个,保证随i的增大而增大,且对于任意i有\(a_{i-1}|a_i\)(显然,\(i\in(1,n]\)),现求将它们划分成最多的组数,并且保证每一组的数字的和大于等于c;\(n\leq 20,c,v\leq 10^8\)

显然要分的组数最多,要能够做到拿出一组中的一个最小的数字,让其不满足条件,即填到尽量满,显然每一组都要尽可能做到。

于是我们从大往小填一组,如果当前枚举的数字为i,如果不选i,而转而选比i小的数字,容易发现因为整除的性质,那些比i小的数字必然会凑成i,更进一步,也就是选i的决策包含了不选i的决策,于是i一定要选。

因此我们就得到了一个凑的满满的组,显然再向其中填入一个数字,一定会超过c,容易知道,要让组数最大,显然需要填入一个当前能填的最小数字,这样满足了一组的和尽可能小,所以最优。

于是我们得到一个做法,从大到小枚举数字,填到不能填为止(这里应该用同余的知识优化,而不是一个一个选),再从小到大选择一个数字,如果总和大于等于c,则++ans。

考虑进一步优化,我们显然一个做法下来,得到了一个组的分配方法,于是我们可以copy,这样就可以做到很快了。

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define il inline
#define ri register
#define Size 25
#define intmax 0x7fffffff
using namespace std;
struct coin{
	int v,b;
	il bool operator<(const coin&a)const{
		return v<a.v;
	}
}co[Size];
int ct,a[Size];
int main(){
	int n,c,ans(0);
	scanf("%d%d",&n,&c);
	for(int i(1),v,b;i<=n;++i){
		scanf("%d%d",&v,&b);
		if(v>=c)ans+=b;
		else co[++ct]={v,b};
	}sort(co+1,co+ct+1);
	while(true){int X(c);
		for(int i(ct),j;i;--i){
			j=min(X/co[i].v,co[i].b);
			a[i]=j,X-=j*co[i].v,co[i].b-=j;
		}
		if(X)for(int i(1);i<=ct;++i)
				 if(co[i].b){++a[i],--co[i].b,X-=co[i].v;break;}
		if(X>0)break;++ans;int opt(intmax);
		for(int i(1);i<=ct;++i)
			if(a[i])opt=min(opt,co[i].b/a[i]);ans+=opt;
		for(int i(1);i<=ct;++i)co[i].b-=opt*a[i];
	}printf("%d",ans);
	return 0;
}
posted @ 2019-08-23 22:41  a1b3c7d9  阅读(260)  评论(0编辑  收藏  举报