编辑:我没有注意到你不能使用硬币作为主要动作,除非它显示出反面。这确实使秩序变得重要。我将把这个答案留在这里,但也考虑再写一个。
这里没有伪代码,但请想一想:你能想象自己掷硬币两次吗?会有什么效果?
或者,写下一些任意的板(字面意思,写下来)。设置一些真实世界的硬币,并选择两个任意的,X 和 Y。做一个“X 翻转”,然后一个“Y 翻转”,然后另一个“X 翻转”。写下结果。现在将电路板重置为初始版本,然后进行“Y 翻转”。比较结果,想想发生了什么。多试几次,有时 X 和 Y 靠得很近,有时不行。对你的结论充满信心。
这种思路应该会引导您找到一种方法来确定一组有限的可能解决方案。您可以相当轻松地测试所有这些。
希望这个提示不要太明显 - 我会密切关注这个问题,看看您是否需要更多帮助。这是一个很好的谜题。
至于递归:您可以使用递归。就个人而言,我不会在这种情况下。
编辑:实际上,再三考虑我可能会使用递归。它可以让生活变得更简单。
好吧,也许这还不够明显。让我们标记硬币 A-P,如下所示:
ABCD
EFGH
IJKL
MNOP
翻转 F 将始终涉及以下硬币更改状态:BEFGJ。
翻转 J 将始终涉及以下硬币更改状态:FIJKN。
如果你掷硬币两次会发生什么?无论发生什么其他翻转,这两个翻转都会相互抵消。
也就是说,先翻F再翻J和翻J再翻F是一样的。先翻F再翻J再翻F和先翻J是一样的。
所以任何解决方案都不是真正的“翻转 A 然后 F 然后 J”的路径 - 它是“翻转 ;不要翻转 ”。 (不幸的是,“翻转”这个词被用于翻转主要硬币和改变特定移动状态的辅助硬币,但没关系 - 希望我的意思很清楚。)
每个硬币都将用作或不用作主要移动,0 或 1。有 16 个硬币,因此有 2^16 种可能性。所以 0 可能代表“什么都不做”; 1 可能代表“只是 A”; 2 可能代表“只是 B”; 3“A和B”等
测试每个组合。如果(不知何故)有多个解决方案,请计算每个解决方案中的位数以找到最少的位数。
实现提示:“当前状态”也可以表示为 16 位数字。使用特定硬币作为主要移动将始终将当前状态与固定数字(对于该硬币)进行异或。这使得计算任何特定动作组合的效果变得非常容易。
好的,这是 C# 中的解决方案。它显示了它找到的每个解决方案需要多少移动,但它不会跟踪那些移动是什么,或者移动的最少数量是多少。这是一个 SMOP :)
输入是一个列表,其中列出了哪些硬币开始出现反面 - 因此对于问题中的示例,您将使用“BEFGJLOP”参数启动程序。代码:
using System;
public class CoinFlip
{
// All ints could really be ushorts, but ints are easier
// to work with
static readonly int[] MoveTransitions = CalculateMoveTransitions();
static int[] CalculateMoveTransitions()
{
int[] ret = new int[16];
for (int i=0; i < 16; i++)
{
int row = i / 4;
int col = i % 4;
ret[i] = PositionToBit(row, col) +
PositionToBit(row-1, col) +
PositionToBit(row+1, col) +
PositionToBit(row, col-1) +
PositionToBit(row, col+1);
}
return ret;
}
static int PositionToBit(int row, int col)
{
if (row < 0 || row > 3 || col < 0 || col > 3)
{
// Makes edge detection easier
return 0;
}
return 1 << (row * 4 + col);
}
static void Main(string[] args)
{
int initial = 0;
foreach (char c in args[0])
{
initial += 1 << (c-'A');
}
Console.WriteLine("Initial = {0}", initial);
ChangeState(initial, 0, 0);
}
static void ChangeState(int current, int nextCoin, int currentFlips)
{
// Reached the end. Success?
if (nextCoin == 16)
{
if (current == 0)
{
// More work required if we want to display the solution :)
Console.WriteLine("Found solution with {0} flips", currentFlips);
}
}
else
{
// Don't flip this coin
ChangeState(current, nextCoin+1, currentFlips);
// Or do...
ChangeState(current ^ MoveTransitions[nextCoin], nextCoin+1, currentFlips+1);
}
}
}