Максимальное значение почтовых марок на конверте

проблема почтовой марки-это математическая загадка, которая спрашивает, Что такое наименьшее почтовое значение, которое нельзя поместить на конверт, если письмо может содержать только ограниченное количество марок, и они могут иметь только определенные заданные номиналы.

например, предположим, что конверт может содержать только три марки, А доступные значения марок-1 цент, 2 цента, 5 центов и 20 центов. Тогда решение составляет 13 центов; так как любое меньшее значение может быть получено не более три марки (например 4 = 2 + 2, 8 = 5 + 2 + 1, etc.), но чтобы получить 13 центов, нужно использовать не менее четырех марок.

есть ли алгоритм, который, учитывая максимальное количество марок и номинальную стоимость марок, можно найти наименьшую почтовую оплату, которая не может быть размещена на конверте?

еще пример:
Можно использовать не более 5 марок
Ценится: 1, 4, 12, 21
Наименьшее значение, которое не может быть достигнуто составляет 72. Значения 1-71 могут быть созданы с определенная комбинация.

в конце концов, я, вероятно, буду использовать Java для кодирования этого.

5 ответов


Да, есть такой алгоритм. Наивно: начиная с 1, пробуйте все возможные комбинации марок, пока не найдете комбинацию, которая дает сумму 1, затем пробуйте для 2 и так далее. Ваш алгоритм завершается, когда он находит число, такое, что никакая комбинация штампов не добавляет к этому числу.

хотя, возможно, медленно, для небольших проблем (скажем, 100 марок, 10 позиций) вы можете решить это за "разумное" количество времени...

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

Если у вас действительно большие проблемы, вам нужно найти способы ускорить поиск, эти ускорения называются эвристикой. Вы не можете победить проблему, но вы можете решите проблему быстрее, чем наивный подход, применяя какие-то знания области.

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


вот еще один совет: каждый набор штампов, который складывается до некоторого заданного числа, может быть сформирован путем добавления 1 штампа к набору штампов минимального размера, который складывается меньше этого числа.

например, предположим, что у нас есть марки 1, 2, 7, 12 и 50 и предел 5 марок, и мы хотим узнать, можно ли представить 82. Чтобы получить 82, вы должны добавить:

  • 1 к набору марок, добавляющих до 82-1=81, или
  • 2 до набор марок, добавляющих до 82-2=80, или
  • 7 к набору марок, добавляющих до 82-7=75, или
  • A 12 В набор марок, добавляя до 82-12=70, или
  • 50 к набору марок, складывающихся до 82-50=32.

Это единственные возможные способы формирования 82. среди всех этих 5 возможностей, один (или, возможно, более одного) будет иметь минимальное количество марок. Если это минимальное число > 5, то 82 не может быть представлено со штампами.

Также обратите внимание, что если число может быть представлено, вам нужно записать минимальное количество марок, необходимых для него, чтобы вычисления для более высоких чисел могли его использовать.

Это ответ Стива Джессопа, надеюсь, ваш ум на правильном пути для решения динамического программирования... Если вы все еще в тупике, дайте мне знать.


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

в Примере, который вы дали в комментарии, на конверт помещается 10 марок, и ни одна марка не имеет значения больше 100. Есть n^10 комбинации штампов, где n количество доступных наименований марок. Но максимально возможная сумма в 10 марок составляет всего 1000. Создайте массив до 1001 и попробуйте придумать эффективный способ разработать для всех этих значений вместе наименьшее количество штампов, необходимых для каждого из них. Ваш ответ - это наименьший индекс, требующий 11 (или более) марок, и вы также можете закрыть каждый штамп на 11.

" эффективный "в этом случае в основном означает:"избегайте повторения любой работы, которую вам не нужно". Так вы захотите повторно использовать промежуточные результаты как можно больше.

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


может быть, это немного бесполезно, чтобы просто дать "подсказки" о решении DP, когда есть предположение, что он даже существует. Итак, вот выполняемый код Perl, реализующий фактический алгоритм DP:

#!/usr/bin/perl
my ($n, @stamps) = @ARGV;
my @_solved;        # Will grow as necessary

# How many stamps are needed to represent a value of $v cents?
sub solve($) {
    my ($v) = @_;
    my $min = $n + 1;

    return 0 if $v == 0;

    foreach (@stamps) {
        if ($v >= $_) {
            my $try = $_solved[$v - $_] + 1;
            $min = $try if $try < $min;
        }
    }

    $_solved[$v] = $min;
    return $min;
}

my $max = (sort { $a <=> $b } @stamps)[-1];

# Main loop
for (my $i = 0; $i <= $max * $n; ++$i) {
    my $ans = solve($i);
    if ($ans > $n) {
        print "$i cannot be represented with <= $n stamps of values " . join(", ", @stamps) . ".\n";
        last;
    }
}

обычно solve() потребует рекурсивного вызова, но поскольку мы всегда пробуем значения в порядке 0, 1, 2, 3... мы можем просто использовать @_solved массив непосредственно, чтобы получить ответ для меньших размеров проблемы.

Это занимает 93ms на моем компьютере, чтобы решить дело для штампа размеры 1, 4, 12, 21 и размер конверта 1000. (Ответ 20967.) Скомпилированный язык будет еще быстрее.


        import java.util.ArrayList;
        import java.util.List;
        /**
         * 
         * @author Anandh
         *
         */
        public class MinimumStamp {

            /**
             * @param args
             */
            public static void main(String[] args) {
                // TODO Auto-generated method stub
                int stamps[]={90,30,24,15,12,10,5,3,2,1};
                int stampAmount = 70;
                List<Integer> stampList = minimumStamp(stamps, stampAmount);
                System.out.println("Minimum no.of stamps required-->"+stampList.size());    
                System.out.println("Stamp List-->"+minimumStamp(stamps, stampAmount));  
            }

            public static List<Integer> minimumStamp(int[] stamps, int totalStampAmount){
                List<Integer> stampList =  new ArrayList<Integer>();
                int sumOfStamps = 0;
                int remainingStampAmount = 0;
                for (int currentStampAmount : stamps) {
                    remainingStampAmount = totalStampAmount-sumOfStamps;
                    if(remainingStampAmount%currentStampAmount == 0){
                        int howMany = remainingStampAmount / currentStampAmount;
                        while(howMany>0){
                            stampList.add(currentStampAmount);
                            howMany--;
                        }
                        break;
                    }else if(totalStampAmount == (sumOfStamps+currentStampAmount)){
                        stampList.add(currentStampAmount);
                        break;
                    }else if(totalStampAmount > (sumOfStamps+currentStampAmount) ){
                        int howMany = remainingStampAmount / currentStampAmount;
                        if(howMany>0){
                            while(howMany>0){
                                stampList.add(currentStampAmount);
                                sumOfStamps += currentStampAmount;
                                howMany--;
                            }
                        }else{
                            stampList.add(currentStampAmount);
                            sumOfStamps += currentStampAmount;
                        }
                    }
                }
                return stampList;
            }
        }