分配珠子拼图的算法(2)?

时间:2016-02-21 17:48:00

标签: algorithm dynamic-programming graph-theory knapsack-problem

假设您有一个带 N 插槽的圆圈(如下所示)。 enter image description here 您的目标是在每个插槽中最终获得指定数量的珠子,并且您有一个大小 N 的阵列,其中包含每个插槽中所需的珠子数量。例如,如果阵列是{1,5,3},那么你需要在插槽1中最终得到1个珠子,插槽2中有5个珠子,插槽3中有3个珠子。你有无限量的珠子。

您可以“解锁” X 广告位。解锁插槽后,您可以开始在该插槽中放置珠子。您可以移动已经在插槽中的珠子,但只能顺时针移动。

为了解决问题,珠子必须移动的最小距离是多少?

以下是一个例子:

N = 6,X = 2.数组:{2,5,4,2,6,2}

解锁插槽2和5.将11个珠子放入插槽2并行进总距离8以到达插槽2,3和4.将10个珠子放入插槽5并行进总距离6以到达插槽5,6和1. 8 + 6 = 14,所以答案是14。

1 个答案:

答案 0 :(得分:0)

问题有一些注意事项:

  • 将珠子移动到(或超出)另一个解锁槽是没有好处的,因为如果那些珠子在另一个未锁定的槽中开始,移动次数会更小;
  • 因此,一旦选择要解锁的插槽,就会确定放入其中的珠子数量以及移动次数;
  • 如果已经针对特定的一组解锁时隙计算了成本(移动次数),则可以容易地导出相邻配置(其中先前解锁时隙保持锁定但下一个时隙被解锁)的成本,无需从头开始计算;
  • 在最佳解决方案中,必须始终解锁必须接收最多bean的插槽;
  • 如果一个插槽选择保持可变,则成本将在选择下一个插槽的方向上增加或减少,这是不正确的;它可以上下起伏,再次上升。

此处建议的算法将遍历所有combinations个可能的插槽选择,并选择具有最低移动的组合。因此时间复杂度 O(n!/ [(n-x)!x!])

我认为应该有一个更有效的算法,不需要访问所有组合,但我没有找到任何允许这种情况的数学模式。

以下是JavaScript代码段中的算法:



function optimalCollectors(beadCounts, collectorCount) {
    // Initialisation
    var n = beadCounts.length;
    if (n < collectorCount) return {error: "not enough slots"};
    var beads = beadCounts.reduce(function (beads, beadCount) {
        return beads + beadCount;
    });
    var cost = beadCounts.reduce(function (cost, beadCount, idx) {
        return cost + beadCount * (idx+1);
    });
    var solution = {
        cost: cost, // too large, to make sure it gets improved
        slots: [] // numbers of the slots chosen for the solution
    };
    var collectorSlots = Array(collectorCount);

    function findNextCollector(collectorNo, startSlot, cost, beads) {
        var addBeads = 0;
        for (var slot=startSlot; slot<=n-collectorCount+collectorNo; slot++) {
            collectorSlots[collectorNo] = slot;
            // progressively calculate total cost, and number of beads
            // "in front" of the currently tried slot.
            cost -= beads;
            beads += addBeads - beadCounts[slot];
            if (collectorNo == collectorCount - 1) { // all slots chosen
                if (cost < solution.cost) { // found a current best
                    solution.cost = cost;
                    // copy currently selected slot numbers:
                    solution.slots = collectorSlots.slice(0);
                }
            } else {
                findNextCollector(collectorNo+1, slot+1, cost, beads);
            }
            if (collectorNo) {
                cost += beadCounts[slot] * (slot + 1 - startSlot);
            } else {
                cost += beadCounts[slot] * (n - 1);
                addBeads = beadCounts[slot];
            }
        }
    }
    
    findNextCollector(0, 0, cost, beads);
    return solution;
}

function randomInput(n) {
    // The random values are in the range 0..n-1. This is just 
    // a convenient choice, in reality there has not to be a limit.
    return Array.from({length: n}, x => Math.floor(Math.random() * n));
}

// Link with I/O
var beads = document.getElementById('beads');
var collectors = document.getElementById('collectors');
var randomize = document.getElementById('randomize');
var calculate = document.getElementById('calculate');
var output = document.getElementById('output');

// Capture events
randomize.onclick = function() {
    var n = 5 + Math.floor(Math.random() * 7);
    beads.value = randomInput(n).join(',');
    collectors.value = 2 + Math.floor(Math.random() * (n/2-2));
    calculate.onclick();
};

calculate.onclick = function() {
    var beadCounts = beads.value.split(',').map(Number);
    var collectorCount = Number(collectors.value);
    var solution = optimalCollectors(beadCounts, collectorCount);
    if (solution.error) {
        output.textContent = 'Error: ' + solution.error;
        return;
    }
    output.textContent = 
        '\nInput: ' + JSON.stringify(beadCounts) +
        '\nNumber of moves: ' + solution.cost +
        '\nChosen slots (0-based): ' + JSON.stringify(solution.slots);
};
&#13;
Comma-separated list of number of beads per slot:<br/>
<input id="beads" size="30" value="2, 5, 4, 2, 6, 2">
<button id="randomize">Randomize</button><br/>
Number of bead-collecting slots:<br/>
<input id="collectors" size="3" value="2"></br>
<button id="calculate">Find collector slots minimising cost</button></br>
<pre id="output"></pre>
&#13;
&#13;
&#13;