POJ 3040 Allowance

POJ 3040题目大意如下:

(最近做的题总是FJ和他的奶牛。。。)FJ想给他的奶牛奖赏,准备的奖金分成N组,每周发给奶牛的奖金不能少于C,每一组都是同一种面值的硬币,同时还告诉了每一组硬币的数量。现在要去寻找一种最佳方案,使得能够最大周数分发奖金。

还有一点(最近看题总是丢三落四):从最小面值到最大面值,前一面值能够整除后一大的面值。

关于题目给出的这个整除关系,开始没有看到,折磨了半天,后来看到了,却还是不是很懂,所以接下来的东西都是参考别人的代码。当然还是贪心:

1)原则上是尽量满足选中的面值总额可以接近C,只能大于等于,而且不多太多;

2)由1)中可知,挑选的原则是先找大面额的,尽量使得面额总值接近C,接下来用小面额(尽量小,也就是从小到大刷选)的去补充;

以上挑选原则可以保证每次挑选都是最优的方案,原因在于:大面额是小面额的倍数,存在可以用足够量的小面额来代替大面额的情况,那么这样一来就浪费了小面额,所以一开始尽量使用大面额,之后再用小面额补充

代码如下:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <limits>

using namespace std;
const int maxn = 20;

struct Node {
    int value, B;
}deno[maxn];
int N, C;
int per[maxn];

bool camp(const Node& lhs, const Node& rhs) {
    return lhs.value > rhs.value;
}

void solve() {
    int weeks = 0;
    //从大到小排序:按照Value的大小
    sort(deno, deno + N, camp);
    //先将value大于C的组直接拿出来
    for (int i = 0; i < N; i++) {
        if (deno[i].value >= C) {
            weeks += deno[i].B;
            deno[i].B = 0;
        }
        else break;
    }
    //循环处理每一组搭配
    while (true) {
        int sum = C;
        memset(per, 0, sizeof(per));
        //先从大到小挑选,但是挑选时要注意不能使得总的和大于C
        for (int i = 0; i < N; i++) {
            if (deno[i].B == 0) continue;
            int temp = min(deno[i].B, sum / deno[i].value);
            if (sum > temp*deno[i].value) {
                sum -= temp*deno[i].value;
                per[i] = temp;
            }
        }
        //接下来从小到大挑选,挑选的时候总和可以稍微大于C
        for (int i = N - 1; i >= 0; i--) {
            if (deno[i].B <= 0) continue;
            if (sum < 0) break;
            int temp = min(deno[i].B - per[i], (sum + deno[i].value - 1) / deno[i].value);
            sum -= temp*deno[i].value;
            per[i] += temp;
        }
        //如果没有这种情况,那么说明所有情况已经挑选完毕
        if (sum > 0) break;
        
        int ave = numeric_limits<int>::max();
        for (int i = 0; i < N; i++) {
            if (per[i] != 0) ave = min(ave, deno[i].B / per[i]);
        }
        weeks += ave;
        //如果这组搭配存在,那么更新每一组硬币的数量
        for (int i = 0; i < N; i++) if (deno[i].B != 0) deno[i].B -= ave*per[i];
    }
    printf("%d\n", weeks);
}

int main(int argc, const char * argv[]) {
    // insert code here...
    while (scanf("%d %d", &N, &C) != EOF) {
        for (int i = 0; i < N; i++) scanf("%d %d", &deno[i].value, &deno[i].B);
        solve();
    }
    return 0;
}

Accept 648K /0MS

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值