对于九宫格拼图,如果随机打乱的话,有50%的概率出现不可还原的情况。
可以采用求逆序数的方法避免不可还原的情况。
逆序数:即在一个数列中,按照从小到大的顺序排列则是顺序,如果有两个数违反了这一规则,即左边的数大于右边的数,那么这两个数就是一个逆序。
例: 0, 3, 1, 2 这个数列中,(0,3),(0,1),(0,2)都是顺序,(3,1)是一个逆序,(3,2)是一个逆序,(1,2)是顺序,只有两个对组是逆序的,所以这个数列的逆序数是2。
在求解拼图的可还原性时需要把空白块去掉,因为那一块是可以只有移动的。
对于 3 x 3 的拼图,把每一块标号即为0,1,2,3,4,5,6,7,8,去掉空白块8号后,使0,1,2,3,4,5,6,7这个数列的逆序数为偶数即可。
0 | 1 | 2 | 0 | 1 | 2 | |||||
---|---|---|---|---|---|---|---|---|---|---|
3 | 4 | 5 | 3 | 4 | 5 | |||||
6 | 7 | 8 | 7 | 6 | 8 |
如上左图,逆序数为0,此即为还原的标准,右图去掉空白块后,数列为0,1,2,3,4,5,7,6,逆序数为1,这是无法变换成左图的顺序的。
计算逆序数的算法很简单,主要是在于理解用逆序数来避免拼图无法还原的方法。
UINT CPuzzleDlg::CalInverseNum(vector<UINT>& vecIndex)
{
// 最后一个标号是可以移动的,故计算逆序数时应去掉,3x3时去掉标号8,4x4时去掉标号15,
vector<UINT> vecCpIndex = vecIndex;
for (vector<UINT>::iterator Iter = vecCpIndex.begin(); Iter != vecCpIndex.end(); Iter++) {
if (*Iter == m_nLastCtlIdx) {
vecCpIndex.erase(Iter);
break;
}
}
// 逆序数是高等数学里面的概念,即在数列当中前面的数大于后面数的组合,
// 例:0 2 1 3 4 7 5 6,数列中2与1是一个逆序,7与5是一个逆序,7与6是一个逆序
// 依次计算逆序数为:0+1+0+0+0+2+0+0=3,所以这个数列的逆序数为 3;
UINT nInverseNum = 0;
for (UINT i = 0; i < vecCpIndex.size() - 1; i++) {
for (UINT j = i + 1; j < vecCpIndex.size(); j++) {
if (vecCpIndex.at(i) > vecCpIndex.at(j)) {
nInverseNum++;
}
}
}
return nInverseNum;
}
源码链接:九宫格拼图-MFC源码