给出我的连连看算法 + 征集更好的算法……(希望可以看见双向BFS的……)
我的算法是基于穷举路径的。说来很吓人,其实速度非常快。因为连连看只允许最多两次转弯,这样最多也只有三条路径。只需要找出这三条就够了。而这三条肯定有两条,使两点在上面。这样其实我们只需要确定一条路径就够了。先确定两个点包括的区域,然后逐行验证是否有可以到达的路径。这就是我的思路。但是听说BFS(特别是双向BFS)很适合做这种情况。我自己写了下,怎么写怎么觉得麻烦……可能是思维定式吧。我提供自己的核心代码。希望也有高手能给出使用广度优先搜索或者是其他高效的算法。先谢谢了~~
以下的代码,LLKNode其实就是CPoint的typedef。pv是vector<CPoint>的typedef。不知道的人就当它是一个顺序表就可以了。结果用pv返回。返回起始点和结束点还有所有的转折点。(其实最多也就四个点)
cx和cy是游戏盘的最大横竖格子数。因为允许使用盘外来连线,所以点的取值是([-1,cx],[-1,cy])。Loc取棋盘中某个点的状态。NOPAN代表此点没有图片,其余的数字代表图片编号。这里没有使用。
Search代表搜索横竖的连通区域。CheckHLine和CheckVLine用来测试横竖情况下是否连通。两个函数只有很少的区别。Check是调用接口。由用户调用Check,由Check判断点的位置再调用CheckHLine和CheckVLine两个函数。
程序代码:
int LLKPlane::Search( FX f,LLKNode pbeg,LLKNode& pret ) { int bc=0; switch (f) { case LEFT: while (--pbeg.x>=-1 && Loc(pbeg) == NOPAN && ++bc) if (pbeg.x==-1)break; break; case RIGHT: while (++pbeg.x<=cx && Loc(pbeg) == NOPAN && ++bc) if (pbeg.x==cx)break; break; case UP: while (--pbeg.y>=-1 && Loc(pbeg) == NOPAN && ++bc) if (pbeg.y==-1)break; break; case DOWN: while (++pbeg.y<=cy && Loc(pbeg) == NOPAN && ++bc) if (pbeg.y==cy)break; break; } pret=pbeg; return bc; } //检查是否有 "LI" 型的连线,假设p1在左,p2在右 //返回最短路径的长度,如果没有找到路径,返回0 bool LLKPlane::CheckHLine( pv& vec,LLKNode p1,LLKNode p2 ) { LLKNode pp; int p1ub,p2ub,p1db,p2db,beg,end; p1ub=p1.y-Search(UP,p1,pp); p1db=p1.y+Search(DOWN,p1,pp); p2ub=p2.y-Search(UP,p2,pp); p2db=p2.y+Search(DOWN,p2,pp); //连通域不相交 if (p1ub>p2db || p2ub>p1db) return false; //上界,选比较大的 if (p1ub>p2ub) beg=p1ub; else beg=p2ub; //下界,选比较小的 if (p1db<p2db) end=p1db; else end=p2db; for (int i=beg;i<=end;i++) { if (Search(RIGHT,LLKNode(p1.x,i),pp) && (pp.x > p2.x || pp==p2) ) { vec.clear(); vec.push_back(p1); if (i!=p1.y)vec.push_back(LLKNode(p1.x,i)); if (i!=p2.y)vec.push_back(LLKNode(p2.x,i)); vec.push_back(p2); return true; } } return false; } //检查是否有 “匚” 型的连线,假设p1在上,p2在下 //返回最短路径的长度,如果没有找到路径,返回0 bool LLKPlane::CheckVLine( pv& vec,LLKNode p1,LLKNode p2 ) { LLKNode pp; int p1lb,p2lb,p1rb,p2rb,beg,end; p1lb=p1.x-Search(LEFT,p1,pp); p1rb=p1.x+Search(RIGHT,p1,pp); p2lb=p2.x-Search(LEFT,p2,pp); p2rb=p2.x+Search(RIGHT,p2,pp); //连通域不相交 if (p1lb>p2rb || p2lb>p1rb) return false; //上界,选比较大的 if (p1lb>p2lb) beg=p1lb; else beg=p2lb; //下界,选比较小的 if (p1rb<p2rb) end=p1rb; else end=p2rb; for (int i=beg;i<=end;i++) { if (Search(DOWN,LLKNode(i,p1.y),pp) && (pp.y > p2.y || pp==p2) ) { vec.clear(); vec.push_back(p1); if (i!=p1.x)vec.push_back(LLKNode(i,p1.y)); if (i!=p2.x)vec.push_back(LLKNode(i,p2.y)); vec.push_back(p2); return true; } } return false; } bool LLKPlane::Check( LLKNode p1,LLKNode p2,pv& vec ) { if (p1 == p2 || Loc(p1)!=Loc(p2) || Loc(p1)==NOPAN) return false; if (p1.x == p2.x) { if (abs(p2.y-p1.y) == 1) { vec.clear(); vec.push_back(p1); vec.push_back(p2); return true; } if (p1.y > p2.y) //p2在p1上方 return CheckVLine(vec,p2,p1); else //p2在p1下方 return CheckVLine(vec,p1,p2); } else if (p1.y == p2.y) { if (abs(p2.x-p1.x) == 1) { vec.clear(); vec.push_back(p1); vec.push_back(p2); return true; } if (p1.x > p2.x) //p2在p1的左边 return CheckHLine(vec,p2,p1); else //p2在p1的右边 return CheckHLine(vec,p1,p2); } else { if (p1.x > p2.x) if (p1.y > p2.y) //p2在p1的左上 return CheckHLine(vec,p2,p1) || CheckVLine(vec,p2,p1); else//p2在p1的左下 return CheckHLine(vec,p2,p1) || CheckVLine(vec,p1,p2); else if (p1.y > p2.y) //p2在p1的右上 return CheckHLine(vec,p1,p2) || CheckVLine(vec,p2,p1); else //p2在p1的右下 return CheckHLine(vec,p1,p2) || CheckVLine(vec,p1,p2); } }