我想用图表来模拟以下谜题。
酒保给你三个 尺寸分别为1000ml,700ml和400ml的玻璃杯。 700毫升和400毫升的眼镜开始 满满的啤酒,但1000毫升的玻璃杯最初是空的。如果你赢了,你可以获得无限量的免费啤酒 以下游戏: 游戏规则:你可以将啤酒从一个杯子倒入另一个杯子,只在源头停止 玻璃是空的或目的地玻璃已满。如果有一系列的倾泻离开,你就赢了 在700毫升或400毫升玻璃杯中精确200毫升。
我有点不确定如何在图表中翻译此问题。我的想法是,眼镜将由加权的无向图中的节点表示,其中边缘表示玻璃u
可以倒入玻璃v
,而另一种方式是相同的,因此散步将是一系列倾向,将导致正确的解决方案。
然而,这种具有三个单节点和无向边的方法对于Dijkstra的算法或其他贪婪算法来说并不是很有效,而我将用它来解决问题。将pourings的排列建模为图表更合适吗?
答案 0 :(得分:0)
如果我们想用图表对这个问题进行建模,每个节点应该代表啤酒量对眼镜的可能分配。假设我们用这样的对象代表每个玻璃:
{ volume: <current volume>, max: <maximum volume> }
然后起始节点是三个这样的对象的列表:
[ { volume: 0, max: 1000 }, { volume: 700, max: 700 }, { volume: 400, max: 400 } ]
边缘表示将一个玻璃倒入另一个玻璃的动作。为了执行这样的操作,我们选择一个源玻璃和一个目标玻璃,然后计算我们可以从源头向目标注入多少:
function pour(indexA, indexB, glasses) { // Pour from A to B.
var a = glasses[indexA],
b = glasses[indexB],
delta = Math.min(a.volume, b.max - b.volume);
a.volume -= delta;
b.volume += delta;
}
从起始节点开始,我们尝试从每个玻璃杯浇注到每个其他玻璃杯。这些行动中的每一个都导致新的啤酒量分配。我们检查每一个以查看我们是否达到了200的目标量。如果没有,我们将分配推送到队列中。
要找到从起始节点到目标节点的最短路径,我们将新发现的节点推送到队列的头部,并从队列的末尾弹出节点。这可以确保当我们到达目标节点时,它与起始节点相比距离队列中的任何其他节点都不远。
为了能够重建最短路径,我们将每个节点的前任存储在字典中。我们可以使用相同的字典来确保我们不会多次探索节点。
以下是此方法的JavaScript实现。单击下面的蓝色按钮运行它。
function pour(indexA, indexB, glasses) { // Pour from A to B.
var a = glasses[indexA],
b = glasses[indexB],
delta = Math.min(a.volume, b.max - b.volume);
a.volume -= delta;
b.volume += delta;
}
function glassesToKey(glasses) {
return JSON.stringify(glasses);
}
function keyToGlasses(key) {
return JSON.parse(key);
}
function print(s) {
s = s || '';
document.write(s + '<br />');
}
function displayKey(key) {
var glasses = keyToGlasses(key);
parts = glasses.map(function (glass) {
return glass.volume + '/' + glass.max;
});
print('volumes: ' + parts.join(', '));
}
var startGlasses = [ { volume: 0, max: 1000 },
{ volume: 700, max: 700 },
{ volume: 400, max: 400 } ];
var startKey = glassesToKey(startGlasses);
function solve(targetVolume) {
var actions = {},
queue = [ startKey ],
tail = 0;
while (tail < queue.length) {
var key = queue[tail++]; // Pop from tail.
for (var i = 0; i < startGlasses.length; ++i) { // Pick source.
for (var j = 0; j < startGlasses.length; ++j) { // Pick target.
if (i != j) {
var glasses = keyToGlasses(key);
pour(i, j, glasses);
var nextKey = glassesToKey(glasses);
if (actions[nextKey] !== undefined) {
continue;
}
actions[nextKey] = { key: key, source: i, target: j };
for (var k = 1; k < glasses.length; ++k) {
if (glasses[k].volume === targetVolume) { // Are we done?
var path = [ actions[nextKey] ];
while (key != startKey) { // Backtrack.
var action = actions[key];
path.push(action);
key = action.key;
}
path.reverse();
path.forEach(function (action) { // Display path.
displayKey(action.key);
print('pour from glass ' + (action.source + 1) +
' to glass ' + (action.target + 1));
print();
});
displayKey(nextKey);
return;
}
queue.push(nextKey);
}
}
}
}
}
}
solve(200);
&#13;
body {
font-family: monospace;
}
&#13;