我需要找到最长的多米诺骨牌链,给出一组12个随机挑选的多米诺骨牌。我已经递归地生成了多米诺骨牌的所有可能性(使用0到12的面值有91种可能性)。多米诺骨牌由一个“砖”组成,上面有两个正方形:[a | b]其中0 =< a,b< = 12.因此,多米诺骨牌的例子可以是[12,0]或[6,3]等。如果相邻的一半具有相同的值,则可以连接多米诺骨牌。
可以翻转多米诺骨牌以适应比赛。例如,给定[8,4],[9,4]可以翻转以使对[8,4] [4,9]
此课程提供以下(与此问题相关)方法:
void flipEnds(); // Flips the domino
int getLeft() const;
int getRight() const;
bool hasBeenPlayed() const;
void setPlayed(bool value);
因此,此问题的示例数据如下:
myDomino #0: [1 12 ]
myDomino #1: [0 5 ]
myDomino #2: [7 9 ]
myDomino #3: [2 7 ]
myDomino #4: [7 12 ]
myDomino #5: [4 8 ]
myDomino #6: [8 10 ]
myDomino #7: [3 11 ]
myDomino #8: [11 12 ]
myDomino #9: [10 11 ]
myDomino #10: [2 9 ]
myDomino #11: [2 4 ]
这更像是一个数学问题,但我怎样才能找到最长的多米诺链?我认为它必须以递归方式完成。
答案 0 :(得分:3)
一系列多米诺骨牌可以表示为{#3,Y},{#4,N},{#0,Y},......第一个数字是你手中的多米诺骨牌和第二个数字是否是翻转。我们想要检查每一个可能的序列,但是早期修剪显然是非法的。
让我们假设您有一个函数testSequence(sequence)
,其中测试序列是有效的。
保留两个列表,一个当前序列currentSeq
,以及一个尚未选择的多米诺unused
。
递归可能就像
checkAllSeq( currentSeq, unused ) {
foreach( domino in unused ) {
unused2 = unused - domino // remove domino from unused list
seq1 = currentSeq + {domino,true} // add unfliped domino to sequence to test
if( testSequence(seq1) ) {
checkAllSeq(seq1,unused2) // recurse
}
// now do it with the domino flipped
seq2 = currentSeq + {domino,false}
if( testSequence(seq2) ) {
checkAllSeq(seq2,unused2)
}
}
}
我错过了一些事情。这只是测试所有可能的序列,它对结果没有任何作用。
答案 1 :(得分:1)
请注意,总是可以获得可以递归求解的任何问题的迭代解决方案,反之亦然。但我同意递归更容易解决这个问题(通常是这样)。
对于任何递归问题,您必须处理三种情况:初始案例,中间案例和终端案例。在这里,最初的情况是你没有玩多米诺骨牌(所以你可以在任何方向上玩任何多米诺骨牌);中间案例是当你被要求玩一个与最近的多米诺骨牌相匹配的多米诺骨牌时;终端案例是当没有可以添加到最后一个多米诺骨牌或者所有多米诺骨牌都被播放的时候。
你需要跟踪两个多米诺骨牌列表:一个用于当前最知名的匹配,一个用于当前的匹配尝试。
如果你设法玩所有的多米诺骨牌,早点回来;否则最好的输入实际上会花费最长的时间。
这是一种天真的方法,即NP-hard(多米诺骨牌两端具有相同数字的可能性不会显着影响问题;它只会增加每条边的重量0.5);使用启发式方法可能值得。
无论如何,我强烈建议首先对你的多米诺骨牌进行排序,如果有重复的话会出错(记得规范化翻转),因为重复版需要采用一种不同的方法解决问题。
答案 2 :(得分:1)
我认为你可以很容易地将这个问题称为树遍历,以获得一个强力解决方案。
" root"这棵树是你多米诺骨牌的首选。该节点的子节点将是可以添加到其中的每个多米诺骨牌。每个级别下降都会增加一个多米诺骨牌链的长度。
另外,请记住,每个添加的多米诺骨牌都可以添加到" head"或"尾巴"链 - 这将增加给定节点的可能子节点数。
许多连锁店都会被缩短,因为你没有选择 - 换句话说,树中的许多节点都没有孩子。这将加快您的搜索速度。
一旦用这种方式措辞,你的问题是进行树遍历以找到该树中最长的链。听起来像是一个很好的递归应用程序(:
答案 3 :(得分:0)
我也需要这个,所以我编码了它。这是我的解决方案,大致基于上面 Salix alba 的伪代码:
#include <iostream>
#include <string>
using namespace std;
struct Tile
{
int val[2];
};
Tile *tiles = NULL;
int nbtiles = 0;
struct Sequence
{
int flip;
int used;
};
Sequence *seq = NULL;
int *order = NULL;
int maxlevel = 0;
int usage (char *name)
{
cerr << "usage: " << name << " start_value tile_left tile_right {tile_left tile_right} ... " << endl;
return 1;
}
int readTiles (int argc, char *argv[])
{
if ((argc & 0x1) != 0)
return (usage (argv[0]));
if (argc < 4)
return (usage (argv[0]));
nbtiles = (argc - 2) / 2;
tiles = new Tile[nbtiles];
int side = 0;
int itile = 0;
for (int i = 2; i < argc; i++)
{
tiles[itile].val[side] = atoi (argv[i]);
side = 1 - side;
if (side == 0)
{
itile++;
}
}
return 0;
}
int findLongest (int start, int level)
{
int ret = 0;
//The "if" below inefficient, but don't care, done at most nbtiles times
if (level > maxlevel)
{
cout << "new high level " << level;
for (int lev = 0; lev < level; lev++)
{
int used = seq[lev].used;
if (used) order[used] = lev;
}
for (int ind = 1; ind < level; ind++)
{
int lev = order[ind];
int flip = seq[lev].flip;
cout << " (" << tiles[lev].val[flip] << "," << tiles[lev].val[1 - flip] << ")";
}
cout << endl;
maxlevel = level;
}
for (int itile = 0; itile < nbtiles; itile++)
{
if (seq[itile].used == 0)
{
for (int flip = 0; flip < 2; flip++)
{
if (tiles[itile].val[flip] == start)
{
seq[itile].flip = flip;
seq[itile].used = level;
findLongest (tiles[itile].val[ 1 - flip], level + 1);
seq[itile].used = 0;
}
}
}
}
return ret;
}
int main (int argc, char *argv[])
{
int ret = readTiles (argc, argv);
if (ret != 0) return ret;
//for (int i = 0; i < nbtiles; i++)
//cout << tiles[i].val[0] << " " << tiles[i].val[1] << endl;
int start = atoi (argv[1]);
seq = new Sequence[nbtiles];
order = new int[nbtiles];
findLongest (start, 1);
return 0;
}