zoukankan      html  css  js  c++  java
  • [USACO09OCT]津贴Allowance

    洛咕

    POJ

    题意:一共有(N (1 <= N <= 20))种不同面额的硬币.每一个面额都能整除所有比它大的面额.每个星期至少给Bessie零花钱(M(1 <= M <= 100000000)).计算Bessie最多能有多少个星期的零花钱.

    分析:贪心:先尽量用大的面额凑出(M)元.每次尽量正好凑出(M)元.

    其实有了这个贪心策略,剩下的就是模拟了.首先面额大于(M)的硬币,可以直接在输入时就统计入答案,后面不予考虑.然后每次考虑如何凑出(M),并记录我们是如何凑出(M)的,方便最后统计答案时一次性计算这种方式最多能够凑出几个(M).

    我们把剩下的比(M)小的硬币按照金额从小到大排序.首先倒序枚举,在不超过(M)的情况下尽量用大的面额去凑,实在凑不出正好的M元,再顺序枚举,用第一个能使凑出大于M元 一个硬币 去凑这一份.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=25;
    int n,m,tot,ans,num[N];
    struct ppx{int v,b;}a[N];
    inline bool cmp(const ppx &x,const ppx &y){return x.v<y.v;}
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=n;++i){
    		int v=read(),b=read();
    		if(v>=m){ans+=b;continue;}//筛掉大于m元的硬币
    		a[++tot].v=v;a[tot].b=b;
    	}
    	sort(a+1,a+tot+1,cmp);//按照面额从小到大排序
    	while(1){
    		memset(num,0,sizeof(num));//记录此次如何拼出m元的
    		int now=m;//表示此次拼出m元,还至少要凑now元
    		for(int i=tot;i>=1;--i){//先倒序枚举
    			if(a[i].b<=0||a[i].v>now)continue;//该种面额用完了或者用了该种面额会超出m元,就不考虑
    			if(a[i].b>=now/a[i].v){//能用多少就用多少
    				num[i]+=now/a[i].v;//记录用了now/a[i].v个第i中硬币
    				now%=a[i].v;//更新now
    			}
    			else{//能用多少就用多少
    				num[i]+=a[i].b;
    				now-=a[i].b*a[i].v;
    			}
    		}
    //这里强调一下,上面倒序循环我们试图正好凑出m元
    //而下面顺序循环是我们无法正好凑出m元,说明不得不浪费一点钱
    //在这种情况下,我们选择一个只要用一个硬币就能超出now元的面额
    		for(int i=1;i<=tot;++i){//再顺序枚举
    			if(now<=0)break;//已经凑好了m元
    			if(a[i].b<=0||now>a[i].v)continue;//该种面额用完了或者用了该种面额不会超出m元,就不考虑
    			now-=a[i].v;++num[i];
    		}
    		if(now>0)break;//此次不能拼出m元,以后也不能够凑出来了
    		int cnt=1e9;
    		for(int i=1;i<=tot;++i)
    			if(num[i])cnt=min(cnt,a[i].b/num[i]);
    //按照此次凑出m的方式,我们能用cnt次这种方式
    		ans+=cnt;
    		for(int i=1;i<=tot;++i)a[i].b-=cnt*num[i];//更新剩余硬币数量
    	}
    	printf("%d
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    viso图插入Word中大片空白解决办法
    面向对象设计模式中的单例模式和工厂模式
    面向对象知识整理
    require和include的区别及自动加载的定义
    面向对象的三大特性及定义
    重写和重载的区别 (部分内容转自竹木人)
    面向对象的基本概念
    PHP json_encode( ) 函数介绍
    js页面跳转常用的几种方式
    js中页面刷新和页面跳转的方法总结 [ 转自欢醉同学 ]
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11426148.html
Copyright © 2011-2022 走看看