最大产品切割算法

问题描述

我正在查看发布在https://www.geeksforgeeks.org/maximum-product-cutting-dp-36/#:~:text=Given%20a%20rope%20of%20length,is%20more%20than%202%20meters上的问题。

问题陈述是

给出一根长度为n米的绳索,以使所有部分的长度乘积最大化的方式将绳索切成整数长度的不同部分。您必须至少裁切一次。假设绳子的长度超过2米。

可以通过动态编程轻松解决。这是他们的解决方

...

<!-- Bitcoin part -->

<div style="height: 500px">
    <!-- just to make scrolling effect possible -->
    <h2 class="myH2" style="padding-bottom: 50px;">Bitcoin</h2>
    <div  class="text-center" id="myData" style="margin-left:500px; font-weight: bold; color: indianred;"></div>
    </div>
<!-- Bitcoin part ends -->





...

<!-- Function used to display json file -->
<script>
    fetch("currentprice.json")
        .then((response) => response.json())
        .then(appendData)
        .catch(console.error);

    function appendData(data) {
        const dataFragment = document.createDocumentFragment();
        for (const price of Object.values(data.bpi)) {
            dataFragment.appendChild(createPriceElement(price));
        }
        document.getElementById("myData").appendChild(dataFragment);
    }

    function createPriceElement(price) {
        const div = document.createElement("div");
        div.innerHTML = `<table><tr><th>Code&nbsp&nbsp&nbsp&nbsp&nbsp</th><th>Symbol&nbsp&nbsp&nbsp&nbsp&nbsp</th><th>Rate</th><th>Description&nbsp&nbsp&nbsp&nbsp&nbsp</th><th>Rate float</th></tr><tr>
            <td> ${price.code} &nbsp&nbsp&nbsp&nbsp&nbsp <td>   ${price.symbol}&nbsp&nbsp&nbsp&nbsp&nbsp<td>  ${price.rate}&nbsp&nbsp&nbsp&nbsp&nbsp</td><td>  ${price.description} </td><td> ${price.rate_float}</td>
        </tr></table> <br><br>` ;

        return div;
    }


    setInterval(function(){
        reload() // this will run after every 5 seconds
    },5000);
</script>
<footer style="text-align: center; padding: 3px;background-color: crimson;  color: white;">
E...
</footer>
</body>
</html>

我的问题是,为什么内循环仅转到// A Dynamic Programming solution for Max Product Problem int maxProd(int n) { int val[n+1]; val[0] = val[1] = 0; // Build the table val[] in bottom up manner and return // the last entry from the table for (int i = 1; i <= n; i++) { int max_val = 0; for (int j = 1; j <= i/2; j++) max_val = max(max_val,(i-j)*j,j*val[i-j]); val[i] = max_val; } return val[n]; } 而不是i/2是有效的?这似乎是利用对称性。但是max函数也超过了i - 1,似乎我们对j * val[i - j]感到厌恶。

解决方法

让我们在这里暂时退出DP解决方案,因为我认为这是基于解决方案的数学特性。

让我们想象一下,您必须将k条切成长度为n的绳索。这比您所说的问题要严格得多,在问题中您可以进行任意(正数)切割。理想的解决方案是什么样的?如果可以随意切割,而不仅仅是整数切割,最好的答案是将绳索切成k块大小为n / k的碎片。为什么是这样?好吧,假设您不这样做。这意味着有些片段必须大于平均值(例如,其大小至少为n / k +ε),而某些片段必须小于平均值(例如,其大小最多为n / k-ε)。那么这两块的乘积最多为

(n / k +ε)(n / k-ε)

=(n / k) 2 2

请注意,ε越大,即散件尺寸之间的差异越大,则乘积越小。这意味着最好改变这些切割方式,使它们的尺寸更接近平均n / k。

即使切割必须为整数大小,也适用相同的逻辑。例如,想象一下,两块的大小至少相差两个。写下一块的大小至少为m + d,另一块的最大大小为m-d。那么这些块的乘积为m 2 -d 2 ,因此最好使它们尽可能接近其平均值,以使d小。

那么为什么n / 2的上限呢?好吧,上述推论表明,如果要进行k割,我们应该将值保持尽可能的接近,尤其是对于k割,任何块的尺寸都不应大于⌈n/k⌉。在k = 2的情况下,最大片段不得大于⌈k/2⌉。我认为这就是界限的来源。

实际上,我很确定此论点意味着可以更快地解决此问题。与其在每个点上切掉任意数量并谈论进行任意数量的切割,不如对切割的数量进行迭代。对于每一个裁切,我们都知道裁切的尺寸是多少-使裁切的尺寸尽可能接近平均尺寸。无需使用任何DP来执行此操作。用伪代码:

max_cut = 0
for cuts = 2 to n:
    average_rounded_down = n // cuts
    pieces_above_average = n % cuts
    pieces_below_average = cuts - pieces_below_average
    value_of_this_cut = (average_rounded_down) ** pieces_below_average + (average_rounded_down + 1) ** pieces_above_average
    if max_cut < value_of_this_cut:
         max_cut = value_of_this_cut

这在时间O(n)上运行,而忽略了将值提升为不同幂的代价(公平地说,不是O(1))。

我相当确定可以通过使用二进制搜索之类的东西来改进对最佳切割数的搜索,从而进一步改善这一点,但是可惜我现在没有时间来研究如何做到这一点。 。 :-)

,

problem source给出的算法的解释:

我们可以看到有很多子问题可以再次解决, 再次。由于再次调用了相同的子问题,因此出现了此问题 重叠子问题属性。 典型的动态规划(DP)问题,相同的计算结果 通过构造一个临时数组val []可以避免子问题 自下而上的方式。

尽管这从技术上讲可以解释推理,但该解释并不能说明所有内容。原始作者没有很好地解释他们对实际代码的思考过程。因此,任何人所能做的最好的就是猜测他们在想什么。如果仅查看i<=5案例,则可能会争辩说作者看到了这种对称性并将其切断。他们以n=5案例为例,证明了这一论点。

为进一步说明,下图显示了循环迭代时max函数的输出。黄色表示使用j <= i/2而不是j <= i可以跳过的迭代。 j=0行表示循环开始之前的val数组,'-'值表示未初始化的值。

enter image description here

这些结果还将显示它们如何进一步简化程序。结果表明,每大于i的{​​{1}},最大值始终位于4行中。

如果我们看到一些有关此问题的示例,则可以轻松观察到 以下模式。可以反复获得最大积 在尺寸大于4时切割尺寸为3的零件,并保持最后 部分,大小为2或3或4。例如,n = 10,即最大乘积 由3、3、4获得。对于n = 11,最大乘积由 3、3、3、2。