POJ3040贪心

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

题解:

①因为数据量较大,所以不能采用单组模拟的方式,不然必会tle,因此设立了一个used[i]数组来存储一种贪心情况,各种硬币所需要的个数,以便将这种情况的总数一次性加在总周数上。

②贪心策略采用在满足先用大额面值,再用小额面值的前提下,先尽可能接近而不超过规定的津贴值;然后用从小额面值到大额面值,超过但尽可能少超过规定的津贴值进行贪心。

注意:

①面值大于津贴的情况,直接在输入处预处理即可。 

②在考虑used数组时,不要大于本身的数量

#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct coin {
    ll value;
    ll num;
}p[30];
bool cmp(coin a, coin b)
{
    return a.value > b.value;
}
int main(void)
{
    ios::sync_with_stdio(false);
    int N, C;cin >> N >> C;
    ll v, n, tot = 1;
    ll week = 0;
    for (ll i = 1; i <= N; i++)
    {
        cin >> v >> n;
        if (v >= C)
            week += n;
        else
        {
            p[tot].value = v;
            p[tot].num = n;
            tot++;
        }
    }
    sort(p + 1, p + 1 + tot, cmp);
    ll used[30];//存储一次贪心后的结果
    while (1)
    {
        memset(used, 0, sizeof(used));
        ll tmp = C;
        ll sum = 0;
        for (ll i = 1; i <= tot; i++)//贪心策略1,面额从大到小,尽可能接近C,但不超过
        {
            if (p[i].num == 0)continue;
            sum += p[i].num * p[i].value;
            used[i] = min(tmp / p[i].value, p[i].num);
            tmp -= used[i] * p[i].value;
        }
        if (sum < C)break;
        if (tmp != 0)//贪心策略2,面额从小到大,尽可能少超过C
        {
            for (ll i = tot; i >= 1; i--)
            {
                if (p[i].num == 0)continue;
                if (used[i] == p[i].num)continue;
                while (used[i] < p[i].num)
                {
                    used[i]++;
                    tmp -= p[i].value;
                    if (tmp <= 0)
                        break;
                }
                if (tmp <= 0)
                    break;
            }
        }
        ll Min_num = inf;//Min_num表示能执行的这一组used的总数
        for (ll i = 1; i <= tot; i++)
        {
            if (used[i] == 0)continue;
            Min_num = min(Min_num, p[i].num/used[i]);
        }
        week += Min_num;
        for (int i = 1; i <= tot; i++)
            p[i].num -= Min_num * used[i];
    }
    cout << week << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/ZJNU-huyh/p/13224867.html