Почему в данном случае не работает жадный подход?

Я пытаюсь решить следующую проблему SPOJ.

Входные данные: 1. Общий вес определенной суммы денег в монетах, 2. Стоимость и соответствующие веса монет используемой валюты.

Цель состоит в том, чтобы найти минимально возможную денежную стоимость данной суммы денег.

Мой подход состоит в том, чтобы отсортировать монеты валюты по соотношению их стоимости/веса в порядке возрастания, а затем жадно подгонять вес первой монеты как можно больше раз к общей сумме (отслеживая, сколько раз она подходит ), затем максимальное количество раз подгонять вес второй монеты к остатку и т. д. для всех монет или до тех пор, пока остаток не станет равным нулю (если нет, то ситуация невозможна).

Судья говорит, что мой ответ неверен. Не могли бы вы подсказать, что не так с алгоритмом?

Мой код здесь:

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

using namespace std;

typedef unsigned int weight_t;
typedef unsigned int value_t;

struct coin {
    weight_t weight;
    value_t value;
    double value_per_gram;
};

coin make_coin(weight_t weight, value_t value) {
    coin ret;
    ret.weight = weight;
    ret.value = value;
    ret.value_per_gram = (double)(value/weight);
    return ret;
}

bool compare_by_value_per_gram(const coin& coin1, const coin& coin2) {
    return coin1.value_per_gram < coin2.value_per_gram;
}

int main() {
    unsigned int test_cases;
    cin >> test_cases;
    while(test_cases--) {
        unsigned int number_of_coins = 0;
        weight_t empty_pig, full_pig, coins_weight, coin_weight, min_value = 0;
        value_t coin_value = 0;
        vector<coin> coins;
        vector<unsigned int> how_many_coins;
        cin >> empty_pig >> full_pig;
        coins_weight = full_pig - empty_pig;
        cin >> number_of_coins;

        while(number_of_coins--) {
            cin >> coin_value >> coin_weight;
            coins.push_back(make_coin(coin_weight, coin_value));
        }
        sort(coins.begin(), coins.end(), compare_by_value_per_gram);

        how_many_coins.resize(coins.size());
        for(unsigned int i = 0; i < coins.size() && coins_weight > 0; i++) {
            how_many_coins[i] = coins_weight/coins[i].weight;
            coins_weight %= coins[i].weight;
            min_value += coins[i].value * how_many_coins[i];
        }
        if(coins_weight == 0) cout << "The minimum amount of money in the piggy-bank is " << min_value << "." << endl;
        else cout << "This is impossible." << endl;
    }
    return 0;
}

person mirgee    schedule 25.08.2014    source источник
comment
Учтите: жадный алгоритм поиска наименьшего количества монет не будет работать, если у вас есть монеты достоинством в доллар, 30 центов, 25 центов, 10 центов, 5 центов и 1 цент.   -  person Hot Licks    schedule 25.08.2014
comment
В простом случае у вас есть монеты номиналом 2 и 5 (одинаковый вес), вам нужно заплатить 6. Единственное решение здесь — 3 умножить на 2, но вы сначала найдете 5, а потом не сможете найти правильный ответ?   -  person cageman    schedule 26.08.2014
comment
я думаю, вы должны использовать DP вместо жадных.   -  person Jason Hu    schedule 26.08.2014
comment
3 грамма монет в банке, 1 цент 2 грамма монет и 2 цента 3 грамма монет.   -  person n. 1.8e9-where's-my-share m.    schedule 26.08.2014
comment
@mirgee Вы когда-нибудь читали о динамическом программировании? Вы должны найти главу об этом в любой хорошей книге по алгоритмам (например, Skiena).   -  person Colonel Panic    schedule 26.08.2014
comment
@ColonelPanic Спасибо за совет. К сожалению, я слышал об этом. Проблема здесь в том, что я подсознательно добавил предположение о том, что нет монет одного веса и нет монет одного номинала, что ничем не подразумевается! Я применил проблему к единственной известной мне валюте. Забавно, как работает мозг, пытаясь применить ограничения мира, который он знает, к проблеме, где эти ограничения не должны существовать.   -  person mirgee    schedule 26.08.2014


Ответы (1)


Простым встречным примером будут два типа монет (weight,value) = {(2,3), (3,3)} с поросенком весом 4. Вы попытаетесь положить худшую монету весом 3 и не сможете сопоставить вес четырех . Но ситуация вполне возможна с 2*(2,3) монетами,

Это можно решить аналогично задаче о рюкзаке, используя некоторую модификацию < href="http://en.wikipedia.org/wiki/Dynamic_programming" rel="nofollow noreferrer">динамическое программирование решение:

Идея состоит в том, чтобы имитировать исчерпывающий поиск. На каждом этапе вы смотрите на текущую монету-кандидата, и у вас есть два варианта: взять ее или перейти к следующей монете.

f(x,i) = INFINITY          x < 0 //too much weight 
f(0,i) = 0                       //valid stop clause, filled the piggy completely.
f(x,0) = INFINITY          x > 0 //did not fill the piggy
f(x,i) = min{ f(x,i-1) , f(x-weight[i], i) + value[i] } //take minimal guaranteed value
               ^                  ^
        coin is not used      use this coin
            anymore

Вызвать с помощью f(Weight,#coins_in_currency)

Временная сложность этого решения при использовании DP (снизу вверх или сверху вниз) составляет O(n*W), где W — желаемый вес поросенка, а n — количество монет в валюте.

person amit    schedule 25.08.2014
comment
Этот метод исчерпывающего поиска не работает, поскольку для некоторых входных данных он заканчивается сравнением двух бесконечностей. Но решение проблемы неограниченного рюкзака с помощью динамического программирования по вашей ссылке в Википедии - это то, что вам нужно. Вот мое решение. - person mirgee; 28.08.2014
comment
@mirgee Это решение представляет собой небольшую вариацию рюкзака. Основное отличие заключается в том, что рюкзак позволяет иметь предметы, общий вес которых меньше его вместимости, и этой проблемы нет. Если у вас не сработало значит небольшая неточность в формуле, но общий подход правильный или у вас ошибка в коде. - person amit; 28.08.2014