Allowance POJ - 3040 [贪心-其他:模拟,优化]

贪心-其他:模拟,优化

原题:Allowance POJ - 3040

中国话:作为创纪录的牛奶生产的奖励,农场主约翰决定开始给Bessie奶牛一个小的每周津贴。FJ有一套硬币N种(1≤N≤20)不同的面额,每枚硬币是所有比他小的硬币面值的倍数,例如1美分硬币、5美分硬币、10美分硬币和50美分硬币。使用这些硬币,FJ每周至少给Bessie C(1 <= C <=100000000)美分。请你计算他最多能给Bessie几周

思路:
1)从大面值到小面值一次拿钱,能拿多少拿多少。
但是注意不能拿到的钱的总和大于C

2)如果第一步拿到的钱不够C,那么就从小面值到大面值拿钱,能拿多少拿多少。
直到拿到的钱总和大于等于C

3)注意: 直接暴力贪心会超时,需要优化:
每一次求出一种方案之后,下一次很可能依旧是这种方案。
因此可以把这一次方案记录下来,判断每种硬币剩下的数量够不够再做一次这种方案。

如果够,下一次就不用模拟了,直接减少对应数量的硬币数,然后ans++就行
只有当当前硬币数量不足以维持这种方案时,才模拟产生新的方案

//
//  Allowance_POJ3040.cpp
//  workspace
//
//  Created by PDP11 on 2021/3/29.
//
//  直接暴力贪心会超时,需要优化
//
//  每一次求出一种方案之后,下一次很可能依旧是这种方案
//  因此可以把这一次方案记录下来,判断每种硬币剩下的数量够不够再做一次这种方案
//  如果够,下一次就不用模拟了,直接减少对应数量的硬币数,然后ans++就行
//  只有当当前硬币数量不足以维持这种方案时,才模拟产生新的方案


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
typedef long long LL;
struct coin{
    int value,num;
    int cnt;
    bool operator< (const coin &b) const{
        return value>b.value;
    }
};
const int maxn=25;
coin coins[maxn];

int main(){
    int N,C;
    cin>>N>>C;
    for(int i=0; i<N; i++){
        scanf("%d%d", &coins[i].value, &coins[i].num);
    }
    sort(coins, coins+N);

    LL sum=0;
    bool flag=true;
    while(true){
        int need=C,cnt;
        if(flag){
            for(int i=0; i<N; i++){
                // cnt用来记录当前方案使用对应硬币的数量
                coins[i].cnt=cnt=min(need/coins[i].value, coins[i].num);
                coins[i].num-=cnt;
                need-=cnt*coins[i].value;
            }
            
            int i=N-1;
            for(; i>=0; i--){
                cnt=min((need+coins[i].value-1)/coins[i].value, coins[i].num);
                coins[i].cnt+=cnt;
                coins[i].num-=cnt;
                need-=cnt*coins[i].value;
                
                if(need<=0) break;
            }
        }
        else{ // 贪心加速
            for(int i=0; i<N; i++){
                coins[i].num-=coins[i].cnt;
            }
            need=0;
        }
        
        // 判断当前硬币能不能够再做一遍当前方案
        flag=false;
        for(int i=0; i<N; i++)
            if(coins[i].cnt>coins[i].num){
                flag=true;
                break;
            }
        
        sum++;
        if(need>0) break;
    }
    cout<<sum-1;
    
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值