信封上邮票的最大值

发布于 2024-09-25 15:34:24 字数 379 浏览 6 评论 0原文

邮票问题是一个数学谜语,它询问如果信件只能容纳有限数量的邮票,并且这些邮票可能只有某些指定的面值,那么不能放在信封上的最小邮资价值是多少。

例如,假设信封只能容纳三张邮票,可用的邮票面值为 1 分、2 分、5 分和 20 分。那么解就是13美分;因为任何较小的值最多可以用三枚邮票获得(例如,4 = 2 + 2、8 = 5 + 2 + 1 等),但要获得 13 美分,必须至少使用四枚邮票。

有没有一种算法可以在给定允许的最大邮票数量和邮票面值的情况下,找到不能贴在信封上的最小邮资?

另一个例子:
最多可使用 5 个印章
价值:1、4、12、21
无法达到的最小值是 72。可以通过特定组合创建值 1-71。

最后我可能会使用 Java 来编写这个代码。

The postage stamp problem is a mathematical riddle that asks what is the smallest postage value which cannot be placed on an envelope, if the letter can hold only a limited number of stamps, and these may only have certain specified face values.

For example, suppose the envelope can hold only three stamps, and the available stamp values are 1 cent, 2 cents, 5 cents, and 20 cents. Then the solution is 13 cents; since any smaller value can be obtained with at most three stamps (e.g. 4 = 2 + 2, 8 = 5 + 2 + 1, etc.), but to get 13 cents one must use at least four stamps.

Is there an algorithm that given the maximum amount of stamps allowed and the face value of the stamps, one can find the smallest postage that cannot be placed on the envelope?

Another example:
Maximum of 5 stamps can be used
Valued: 1, 4, 12, 21
The smallest value that cannot be reached is 72. Values 1-71 can be created with a certain combination.

In the end I will probably be using Java to code this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

无风消散 2024-10-02 15:34:24

是的,有这样的算法。天真地:从 1 开始,尝试所有可能的邮票组合,直到找到总和为 1 的组合,然后尝试 2,依此类推。当您的算法找到一个数字并且没有任何邮票组合可以添加到该数字时,您的算法就完成了。

尽管可能很慢,但对于足够小的问题(比如 100 个邮票,10 个位置),您可以在“合理”的时间内解决这个问题......

但是,对于大问题,我们有很多可用邮票(比如 1000 个)和很多可能的位置(例如 1000 秒),该算法可能会花费大量时间。也就是说,对于非常大的问题,使用这种方法解决问题的时间可能是宇宙的寿命,因此它对您来说并不是真正有用。

如果您有非常大的问题,您需要找到加速搜索的方法,这些加速称为启发式。你无法解决问题,但通过应用某种领域知识,你可能可以比简单的方法更快地解决问题。

改进这种天真的方法的一个简单方法可能是,每当您尝试不等于您要查找的数字的邮票组合时,您都会从可能的集合中删除该组合以尝试任何未来的数字,并标记该未来号码为不可用。换句话说:保留一个你已经找到的数字和组合的列表,然后不要再次寻找这些数字或它们的组合。

Yes, there is such an algorithm. Naively: starting with 1, try every possible combination of stamps until we find a combination that yields a sum of 1, then try for 2, and so on. Your algorithm finishes when it finds a number such that no combination of stamps adds to that number.

Albeit possibly slow, for small enough problems (say 100 stamps, 10 positions) you can solve this in a "reasonable" amount of time...

But, for large problems, ones where we have many stamps available (say 1000s) and many possible positions (say 1000s), this algorithm might take an intractable amount of time. That is, for very large problems, the time to solve the problem using this approach might be say, the lifetime of the universe, and thus it's not really useful to you.

If you have really large problems you need to find ways to speed up your search, these speedups are called heuristics. You can't beat the problem, but you can possibly solve the problem faster than the naive approach by applying some sort of domain knowledge.

A simple way to improve this naive approach might be that any time you try a combination of stamps that doesn't equal the number you're looking for you remove that combination from the possible set to try for any future number, and mark that future number as unavailable. Said another way: keep a list of numbers you've found already and the combinations that got you there, then don't look for those numbers or their combinations again.

爱你是孤单的心事 2024-10-02 15:34:24

这里还有另一个提示:每组总和达到某个给定数字的邮票都可以通过将 1 个邮票添加到总和小于该数字的最小邮票组中来形成。

例如,假设我们有邮票 1、2、7、12 和 50,并且限制为 5 个邮票,我们想知道是否可以表示 82。要得到 82,您必须添加:

  • A 1 到一组总计为 82-1=81 的邮票,或
  • A 2 到一组总计为 82-2=80 的邮票,或
  • A 7 到一组总计为 82-1=81 的 邮票。邮票加起来为 82-7=75,或
  • A 12 为一套邮票加起来为 82-12=70,或
  • A 50 为一套邮票加起来为 82-50=32。

这些是形成 82 的唯一可能方式。在所有这 5 种可能性中,一种(或可能不止一种)将具有最少数量的印章。如果该最小数量> 5,那么82就不能用邮票来表示了。

另请注意,如果可以表示一个数字,则需要记录该数字所需的最小标记数,以便计算更大的数字时可以使用它。

加上Steve Jessop的回答,希望能让您明白动态编程解决方案的正确方向...如果您仍然感到困惑,请告诉我。

Here is another tip: Every set of stamps that adds up to some given number can be formed by adding 1 stamp to a minimum-sized set of stamps that adds up to less than that number.

For example, suppose we have the stamps 1, 2, 7, 12, and 50, and a limit of 5 stamps, and we want to find out whether 82 can be represented. To get that 82, you must add either:

  • A 1 to a set of stamps adding up to 82-1=81, or
  • A 2 to a set of stamps adding up to 82-2=80, or
  • A 7 to a set of stamps adding up to 82-7=75, or
  • A 12 to a set of stamps adding up to 82-12=70, or
  • A 50 to a set of stamps adding up to 82-50=32.

Those are the only possible ways that 82 can be formed. Among all those 5 possibilities, one (or possibly more than one) will have the minimum number of stamps. If that minimum number is > 5, then 82 can't be represented with stamps.

Notice also that if a number can be represented, you need to record the minimum number of stamps needed for it so that calculations for higher numbers can use it.

This, plus Steve Jessop's answer, will hopefully get your mind on the right track for a dynamic programming solution... If you're still stumped, let me know.

橘亓 2024-10-02 15:34:24

不要详尽地计算所有可能的邮票组合的总和(可能通过递归),而是考虑所有可能的总和,并计算出产生每个总和的最小邮票数量是多少。邮票的组合有很多,但不同的总和却少得多。

在您在评论中给出的示例中,一个信封适合 10 张邮票,没有一张邮票的价值大于 100。邮票有 n^10 种组合,其中 n是可用邮票的面额数量。但 10 个邮票的最大可能总和仅为 1000。创建一个最多 1001 的数组,并尝试想出一种有效的方法来计算出将所有这些值加在一起,制作每个邮票所需的最少邮票数量。那么您的答案就是需要 11 个(或更多)邮票的最少索引,并且您也可以将每个邮票数量限制为 11 个。

在这种情况下,“高效”基本上意味着“避免重复任何不必要的工作”。因此,您将希望尽可能地重用中间结果。

如果这还不够提示,那么(a)我对方法的理解是错误的(在这种情况下,抱歉,在回答之前我自己实际上还没有解决问题)或(b)更新说明你已经走了多远沿着这些思路。

Rather than exhaustively computing the sums of all the possible combinations of stamps (perhaps by recursion), consider all the possible sums, and work out what the smallest number of stamps is to produce each sum. There are loads of combinations of stamps, but a lot fewer distinct sums.

In the example you gave in a comment, 10 stamps fit on an envelope, and no stamp has value greater than 100. There are n^10 combinations of stamps, where n is the number of denominations of stamp available. But the greatest possible sum of 10 stamps is only 1000. Create an array up to 1001, and try to think of an efficient way to work out, for all of those values together, the least number of stamps required to make each one. Your answer is then the least index requiring 11 (or more) stamps, and you can cap each stamp-count at 11, too.

"Efficient" in this case basically means, "avoid repeating any work you don't have to". So you're going to want to re-use intermediate results as much as possible.

If that's not enough of a hint then either (a) I'm wrong about the approach (in which case sorry, I haven't actually solved the problem myself before answering) or (b) update to say how far you've got along those lines.

亽野灬性zι浪 2024-10-02 15:34:24

当有人猜测存在 DP 解决方案时,仅仅给出有关 DP 解决方案的“提示”可能有点无益。所以这里是实现实际 DP 算法的可运行 Perl 代码:

#!/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 数组来获取较小问题的答案。

在我的 PC 上,解决邮票尺寸 1、4、12、21 和信封尺寸 1000 的情况需要 93 毫秒。(答案是 20967。)编译语言会更快。

Maybe it's a bit unhelpful to just give "hints" about a DP solution when there is speculation that one even exists. So here is runnable Perl code implementing the actual DP algorithm:

#!/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;
    }
}

Ordinarily solve() would require a recursive call, but because we always try values in the order 0, 1, 2, 3..., we can just use the @_solved array directly to get the answer for smaller problem sizes.

This takes 93ms on my PC to solve the case for stamp sizes 1, 4, 12, 21 and envelope size 1000. (The answer is 20967.) A compiled language will be even faster.

忆沫 2024-10-02 15:34:24
        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;
            }
        }
        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;
            }
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文