• Allowance


    Allowance

    有n种数字,第i种数字值为(v_i),有(b_i)个,保证随i的增大而增大,且对于任意i有(a_{i-1}|a_i)(显然,(iin(1,n])),现求将它们划分成最多的组数,并且保证每一组的数字的和大于等于c;(nleq 20,c,vleq 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;
    }
    
  • 相关阅读:
    弄懂Java为何只有值传递
    反转链表进阶
    剑指Offer-16:合并两个有序链表
    剑指Offer-15:反转链表
    剑指Offer-14:输入一个链表,输出该链表中倒数第k个结点。
    剑指Offer-13:调整数组位置使奇数位于偶数前面
    Java实现二分查找
    LDAP
    关于Prometheus运维实践项目
    LDAP-openldap服务部署和测试(YUM安装)
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/11403024.html
Copyright © 2020-2023  润新知