http://blog.163.com/prevBlogPerma.do?host=simplesource&srl=10341406200981362416959&mode=prev
这个代码写得相当不错。
这里先对蛮力搜索做个分析。
LightsOffAppDlg.h 添加了些调试内容。
// LightsOffAppDlg.h : 头文件 // #pragma once #include "lightsoff.h" #include "afxwin.h" // CLightsOffAppDlg 对话框 class CLightsOffAppDlg : public CDialog { // 构造 public: CLightsOffAppDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_LIGHTSOFFAPP_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 int m_iUnitWidth; int m_iX0; int m_iY0; CField m_field; CField m_mask; CField m_recorder; bool m_bKillMsg; int m_iState; CMatrix m_trans; CMatrix m_switch; FILE * fp ; // 设置大小 void SetSize() { int iWidth = m_cbWidth.GetCurSel() + 1; int iHeight = m_cbHeight.GetCurSel() + 1; m_field.Create(iWidth, iHeight); m_mask = m_field; m_recorder = m_field; int w, h; int fw = m_iUnitWidth * iWidth; int fh = m_iUnitWidth * iHeight; w = fw + 128; h = fh + 100; if(w < 360) { w = 360; } if(h < 200) { h = 200; } MoveWindow( (::GetSystemMetrics(SM_CXSCREEN) - w) / 2, (::GetSystemMetrics(SM_CYSCREEN) - h) / 2, w, h); CRect rect; GetClientRect(&rect); m_iX0 = 50 + (rect.Width() - fw) / 2; m_iY0 = 50; // 计算求解矩阵 m_switch.Create(iWidth, iHeight); m_trans.Create(iWidth, iHeight); m_trans.SetAsI(); m_switch.SetAsL(m_mask); m_switch.Inverse(m_trans); m_switch.SetAsL(m_mask); Invalidate(); } // 执行线程 static UINT ThreadProc(LPVOID pParm) { CLightsOffAppDlg * pClass = (CLightsOffAppDlg *)pParm; //pClass->m_iMode = PRO_MODE_ASYN; pClass->m_iState = 1; CField fld; fld.Create(pClass->m_field.Width(), pClass->m_field.Height()); pClass->m_recorder = fld; pClass->fp = fopen("m.txt", "w"); if(pClass->fp) { fld.Print(pClass->fp); pClass->m_recorder.Print(pClass->fp); pClass->m_mask.Print(pClass->fp); pClass->m_field.Print(pClass->fp); } if(pClass->FindRecord(fld, pClass->m_recorder)) { pClass->Invalidate(false); if(pClass->m_bKillMsg == false) { pClass->MessageBox("找到解", "成功", MB_ICONINFORMATION); } } else { pClass->Invalidate(false); if(pClass->m_bKillMsg == false) { pClass->MessageBox("未找到解", "失败", MB_ICONERROR); } } pClass->KillTimer(0); pClass->m_iState = 0; pClass->SetDlgItemText(IDC_BT_SEARCH, "蛮力搜索"); //pClass->m_iMode = PRO_MODE_BLOCK; if(pClass->fp) { fclose(pClass->fp); } return 0; } bool FindRecord(CField &fld, CField &rcd, int x = 0, int y = 0) { if(m_bKillMsg) { return false; } fld.AndNot(m_mask); if(fld == m_field) { return true; } if(x >= fld.Width()) { y++; x = 0; if(y >= fld.Height()) { if(fp) { fprintf(fp, "out of range. x=%d, y=%d", x, y); fprintf(fp, "\n"); } return false; } } int i; if(fp) { fprintf(fp, "x=%d, y=%d", x, y); fprintf(fp, "\n"); fld.Print(fp); rcd.Print(fp); m_mask.Print(fp); m_field.Print(fp); } if(m_mask.IsLightOn(x, y)) { fprintf(fp, "m_mask.IsLightOn(%d, %d)", x, y); fprintf(fp, "\n"); if(FindRecord(fld, rcd, x + 1, y)) { return true; } } else { fprintf(fp, "not m_mask.IsLightOn(%d, %d)", x, y); fprintf(fp, "\n"); for(i = 0; i < 2 && !m_bKillMsg; i++) { if(FindRecord(fld, rcd, x + 1, y)) { return true; } if(fp) { fprintf(fp, "x=%d, y=%d", x, y); fprintf(fp, "\n"); fprintf(fp, "i=%d", i); fprintf(fp, "\n"); } fld.LightsOff(x, y); rcd.SetLight(x, y); if(fp) { fprintf(fp, "fld.LightsOff(%d, %d);", x, y); fprintf(fp, "\n"); fld.Print(fp); rcd.Print(fp); m_mask.Print(fp); m_field.Print(fp); } } } return false; } // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnRButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnTimer(UINT nIDEvent); afx_msg void OnBnClickedBtCompute(); CComboBox m_cbWidth; CComboBox m_cbHeight; afx_msg void OnBnClickedBtSearch(); afx_msg void OnCbnSelchangeCbWidth(); afx_msg void OnCbnSelchangeCbHeight(); afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point); afx_msg void OnBnClickedBtReverse(); afx_msg void OnBnClickedBtClear(); };
他这解法,是从右下角开始往回开灯求解的。如果用我这个测试代码,千万别从左上角进行测试。否则,日志信息将极宠大。
以下列出一小段开灯的递归顺序。
44
44
34
44
44
34
24
44
44
34
44
44
34
24
14
44
44
34
44
44
34
24
44
44
34
44
44
34
24
14