
本教程详解如何计算将一组六面骰子全部调整为同一朝上面所需的最小旋转次数,核心在于枚举所有可能出现的目标点数(而非仅选频次最高者),并利用骰子对立面之和为7的规律高效计算每种方案的代价。
本教程详解如何计算将一组六面骰子全部调整为同一朝上面所需的最小旋转次数,核心在于枚举所有可能出现的目标点数(而非仅选频次最高者),并利用骰子对立面之和为7的规律高效计算每种方案的代价。
在解决“最小骰子旋转次数”问题时,关键误区在于:仅选择数组中出现频率最高的数字作为目标值是错误的。虽然该策略看似合理(减少需改动的骰子数量),但它忽略了旋转代价的差异性——将一个骰子从 1 旋转到 6 需要 2 次旋转(因 6 是 1 的对立面),而旋转到 2、3、4 或 5 均只需 1 次。因此,即使某数字频次略低,若其作为目标能大量复用“单步旋转”或避开“双步对立旋转”,总代价反而更优。
正确解法是:枚举所有可能的目标朝上点数,但无需遍历 1~6 全集,而只需考察输入数组中实际出现过的唯一值(new Set(diceArray))。原因在于:若某个数字 x 未出现在原数组中,则将所有骰子转为 x 必然比转为某个已存在的 y 代价更高(至少不更优)——因为每个骰子都至少需 1 次旋转,而存在 y 时至少有一个骰子无需旋转。
? 旋转代价规则(基于标准骰子结构)
- 同一面 → 0 次旋转
- 相邻面(非对立)→ 1 次旋转
- 对立面(如 1↔6, 2↔5, 3↔4)→ 2 次旋转
✅ 巧妙观察:所有对立面对均满足
a + b === 7。因此无需维护映射表,用diceValue + target === 7即可瞬时判断是否为对立面。
✅ 正确实现代码(简洁高效版)
function rotations(diceArray) {
const uniqueTargets = [...new Set(diceArray)];
let minRotations = Infinity;
for (const target of uniqueTargets) {
let cost = 0;
for (const face of diceArray) {
if (face === target) continue; // 0 rotations
if (face + target === 7) cost += 2; // opposite face → 2 rotations
else cost += 1; // adjacent face → 1 rotation
}
minRotations = Math.min(minRotations, cost);
}
return Number.isFinite(minRotations) ? minRotations : 0;
}
? 示例验证
输入:[1, 1, 1, 1, 1, 6]
- 尝试
target = 1:五个1(0 代价)+ 一个6(1+6===7→ 2 代价)→ 总代价 = 2 - 尝试
target = 6:五个1(1+6===7→ 各 2 代价)+ 一个6(0 代价)→ 总代价 = 10
✅ 最小值为2,算法正确返回。
⚠️ 注意事项
-
不要盲目枚举 1~6:虽骰子只有 6 面,但若输入为
[2, 2, 2],尝试target=1的代价(3×1=3)必然 ≥ 尝试target=2(0);枚举uniqueTargets可将时间复杂度稳定控制在 O(n·k)(k ≤ 6,实际远小于 n)。 -
边界处理:空数组?题目隐含
diceArray.length ≥ 1,但为健壮性保留Number.isFinite检查。 - 性能无虞:最坏情况(6 个不同数字)也仅 6×n 次操作,对 Codewars 测试规模完全足够。
掌握这一思路,不仅可解此题,更能迁移至同类“最小变换代价”问题:当操作代价非均匀时,贪心(频次优先)失效,必须全量评估可行目标状态。










