近日,我班吴娜给我出了一个智力题:一个5*5的方阵(如图所示),要求只能横、竖走,且每个圈只能经过一次,问能不能一笔把所有的圈都画过来。
OOOOO
OOOOO
OOOOO
OOOOO
O OOO
苦思良久,不得其解,便想编写一程序,让计算机遍历所有的分支,求出答案。可程序成败的关键是能把所有分支都遍历到。故把代码贴出,望网友指出其不对的地方。(程序运行后并没有找到一正确的分支,所以怀疑其题目是否有解,也或程序设计得不对)
该程序启用了辅助线程进行计算,所以运行后不会出现对话框无响应的情况。
算法如下: 下载EXE文件
#define X(p) (p)%6
#define Y(p) ((p)/6-1)
BYTE g_data[42]=
{// g_data 数组用以保存5*5的方阵信息,1为可走的圈,0为不可走,之所以上、下、右设0,是因为这样可以避免越界的判断,提高程序效率。
0,0,0,0,0,0,
1,1,1,1,1,0,
1,1,1,1,1,0,
1,1,1,1,1,0,
1,1,1,1,1,0,
1,0,1,1,1,0,
0,0,0,0,0,0
};
int g_dd[4]={-1,-6,1,6};// g_dd 为当前圈的左、上、右、下四个方向增量
CByteArray g_wd;// g_wd 用来保存走过的节点
BYTE g_first,g_curr; //g_first为起点(这儿假设为左下角的圈,即g_data[30]);g_curr为当前走到的节点位置
int GetWay(int pos)//得到pos节点当前所走的方向。result:0-左;1-上;2-右;3-下
{
int result;
switch(g_wd.GetAt(pos+1)-g_wd.GetAt(pos))
{
case -1:
result=0;
break;
case -6:
result=1;
break;
case 1:
result=2;
break;
case 6:
result=3;
break;
}
return result;
}
int cango(int pos)//pos节点是否还有其它方向可走
{
for(int i=GetWay(pos)+1;i<4;i++)//从pos节点走过的下一方向开始计算
{
if(g_data[g_wd.GetAt(pos)+g_dd[i]])//下一方向可走则返回该方向值
return i;
}
return 0;
}
CWinThread *g_pThread=NULL;//计算线程指针
UINT Think(LPVOID p)//遍历函数
{
g_first=30;
g_curr=30;
g_wd.RemoveAll();
g_wd.Add(g_first);
int next;
int cugo=0;
ConedrawDlg *dlg=(ConedrawDlg *)p;
do
{
for(int i=cugo;i<4;i++)
{
next=g_curr+g_dd[i];//next为i[0,3]方向上的下一节点
if(g_data[next])//g_data[next]=1表示i方向上下一节点可走,0为不可走
{
g_curr=next;//更新当前节点值
g_wd.Add(g_curr);//将当前节点添加进队列
g_data[g_curr]=0;//当前节点设为不可走
//更新结果窗口
dlg->InvalidateRect(CRect(50,50,230,230),FALSE);
dlg->UpdateWindow();
break;
}
}
cugo=0;
if(g_wd.GetSize()==24)//走过的节点数为24个
{
dlg->MessageBox("此题有解!");
g_pThread=NULL;
return 0;
}
if(i==4)//0-3四个方向都无路可走,则退回前面的某个节点
{
int l=(int)g_wd.GetUpperBound();//当前走过的最后一个节点
int fir=l;
do
{
g_data[g_wd.GetAt(l)]=1; //g_wd.GetAt(l)节点可走
l--;
if(l==0)//退到起点则表示此题无解
{
for(int m=fir;m>l;m--)//从队列中删除退回的一段节点
g_wd.RemoveAt(m);
dlg->Invalidate();
dlg->UpdateWindow();
dlg->MessageBox("此题无解!");
dlg->GetDlgItem(IDC_BUTTON2)->SetWindowText("重新计算");
g_pThread=NULL;
return 0;
}
g_curr=g_wd.GetAt(l);
}while(!(cugo=cango(l)));//前一节点是否还有其它方向可走
for(int m=fir;m>l;m--)//从队列中删除退回的一段节点
g_wd.RemoveAt(m);
}
}while(g_wd.GetSize()<24);
return 0;
}