多米诺覆盖问题的回溯解法

题目:现有6*6大小的棋盘和18张多米诺骨牌,每张牌能覆盖2个棋格,求将多米诺骨牌完全覆盖棋盘的所有组合?

 

 

#include <iostream>
#include <string>
#include <map>
#include <stack>
#include <set>
#include <sstream>

using namespace std;

const int MAX_LENGTH = 2 * 3;
set<string> result;

//=================
// 棋盘位置类
//=================
class Position
{
private:
    int x;
    int y;

public:
    Position() : x(-1), y(-1)
    {
    }

    Position(const Position& position) : x(position.getX()), y(position.getY())
    {
    }

    Position(int x, int y) : x(x), y(y)
    {
        if (x < 0 || x >= MAX_LENGTH)
        {
            x = -1;
        }
        if (y < 0 || y >= MAX_LENGTH)
        {
            y = -1;
        }
    }

    int getX() const
    {
        return x;
    }

    int getY() const
    {
        return y;
    }

    Position getRight()
    {
        return Position(x, y + 1);
    }

    Position getBottom()
    {
        return Position(x + 1, y);
    }

    bool valid() const
    {
        return x >= 0 && x < MAX_LENGTH && y >= 0 && y < MAX_LENGTH;
    }

    bool operator==(const Position& right) const
    {
        return x == right.getX() && y == right.getY();
    }
};

//=================
// 棋子类
//=================
class Chessman
{
private:
    pair<Position, Position> positions;

public:
    Chessman()
    {
    }
    
    Chessman(const pair<Position, Position>& positions) : positions(positions)
    {
    }

    pair<Position, Position> getPositions() const
    {
        return positions;
    }

    bool valid() const
    {
        if (!positions.first.valid() || !positions.second.valid())
        {
            return false;
        }

        if (positions.first == positions.second)
        {
            return false;
        }
        return true;
    }

    bool isXdirection() const
    {
        if (!valid())
        {
            return false;
        }
        return positions.first.getX() == positions.second.getX();
    }
};

//=================
// 棋盘类
//=================
class Chessboard
{
private:
    size_t board[MAX_LENGTH][MAX_LENGTH];
    stack<Chessman> chessmen;

public:
    Chessboard()
    {
        for (int i = 0; i < MAX_LENGTH; i++)
        {
            for (int j = 0; j < MAX_LENGTH; j++)
            {
                board[i][j] = 0;
            }
        }
    }

    bool hold(const Chessman& chessman)
    {
        pair<Position, Position> positions = chessman.getPositions();

        Position first = positions.first;
        if (!first.valid() || board[first.getX()][first.getY()])
        {            
            return false;
        }
        
        Position second = positions.second;
        if (!second.valid() || board[second.getX()][second.getY()])
        {            
            return false;
        }

        board[first.getX()][first.getY()]   = chessmen.size() + 1;
        board[second.getX()][second.getY()] = chessmen.size() + 1;

        chessmen.push(chessman);

        if (isFinished())
        {
            string solution = getSolutionString();
            result.insert(solution);
            cout<<"Solution Num : "<<result.size()<<endl;
            cout<<solution<<endl;
            cout<<"====================================="<<endl;
        }

        return true;
    }

    bool unhold(Chessman& pop)
    {
        pop = chessmen.top();
        board[pop.getPositions().first.getX()][pop.getPositions().first.getY()]   = 0;
        board[pop.getPositions().second.getX()][pop.getPositions().second.getY()] = 0;
        chessmen.pop();

        return true;
    }

    bool isHold(const Position& position) const
    {
        if (position.valid())
        {
            if (board[position.getX()][position.getY()])
            {
                return true;
            }
        }
        return false;
    }

    bool isFinished() const
    {
        return chessmen.size() == MAX_LENGTH * MAX_LENGTH / 2;
    }

    string getSolutionString()
    {
        ostringstream rtn;
        for (int i = 0; i < MAX_LENGTH; i++)
        {
            for (int j = 0; j < MAX_LENGTH; j++)
            {
                if (i != 0 && j == 0)
                {
                    rtn<<endl;
                }
                rtn.width(4);
                rtn<<board[i][j];                
            }
        }
        return rtn.str();
    }  
};

int main(int argc, char** argv)
{
    Chessboard board;

    bool done = false;
    for (int i = 0; i < MAX_LENGTH && !done; i++)
    {
        for (int j = 0; j < MAX_LENGTH && !done; j++)
        {
            if (!board.isFinished())
            {     
                Position currgrid(i, j);   
                if (board.isHold(currgrid))
                {
                    continue;
                }
                Chessman chessmanR(make_pair(currgrid, currgrid.getRight()));
                Chessman chessmanB(make_pair(currgrid, currgrid.getBottom()));

                if (board.hold(chessmanR) || board.hold(chessmanB))
                {
                    continue;
                }
            }

            while (true)
            {
                Chessman pop;
                board.unhold(pop);
                
                Position popposition = pop.getPositions().first;
                i = popposition.getX();
                j = popposition.getY();

                if (pop.isXdirection())
                {
                    Chessman change(make_pair(popposition, popposition.getBottom()));
                    if (board.hold(change))
                    {
                        break;
                    }
                }

                if (i == 0 && j == 0)
                {
                    done = true;
                    break;
                }
            }
        }
    }

    cout<<"OK, Done."<<endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
棋盘覆盖问题是指在一个大小为2^n * 2^n的正方形棋盘中,有一个方格被去掉(如下图所示),现在需要用L型骨牌(类似于多米诺骨牌,由两个小正方形组成,形状如“L”)将棋盘完全覆盖,求覆盖方案。 ![棋盘覆盖问题示意图](https://img-blog.csdn.net/20150923193807438) MFC是微软基于Windows API的一个应用框架,用于快速开发Windows桌面应用程序。实现棋盘覆盖问题需要使用递归算法,MFC可以很方便地实现递归算法,以下是一个简单的MFC程序实现棋盘覆盖问题: 1. 创建一个MFC应用程序,选择对话框为基础模板。 2. 在对话框中添加一个按钮,用于触发覆盖棋盘的操作。 3. 在对话框类的头文件中添加以下代码: ```cpp #define MAXN 1024 // 棋盘大小的最大值 int board[MAXN][MAXN]; // 棋盘 int tile = 1; // 骨牌编号 ``` 4. 在对话框类的.cpp文件中添加以下代码: ```cpp // 将区域(x1, y1)~(x2, y2)覆盖成L型骨牌,除了(r, c)这个位置 void cover(int x1, int y1, int x2, int y2, int r, int c) { if (x1 == x2 && y1 == y2) return; // 区域大小为1,不需要覆盖 int s = tile++; // 当前使用的骨牌编号 int xm = (x1 + x2) / 2, ym = (y1 + y2) / 2; // 中心点坐标 // 覆盖左上角区域 if (r <= xm && c <= ym) cover(x1, y1, xm, ym, r, c); else { board[xm][ym] = s; // 覆盖其余三个区域 cover(x1, y1, xm, ym, xm, ym); if (r <= xm && c > ym) cover(x1, ym + 1, xm, y2, r, c); if (r > xm && c <= ym) cover(xm + 1, y1, x2, ym, r, c); if (r > xm && c > ym) cover(xm + 1, ym + 1, x2, y2, r, c); } // 覆盖右上角区域 if (r <= xm && c > ym) cover(x1, ym + 1, xm, y2, r, c); else { board[xm][ym + 1] = s; // 覆盖其余三个区域 if (r <= xm && c <= ym) cover(x1, y1, xm, ym, xm, ym); cover(x1, ym + 1, xm, y2, xm, ym + 1); if (r > xm && c <= ym) cover(xm + 1, y1, x2, ym, r, c); if (r > xm && c > ym) cover(xm + 1, ym + 1, x2, y2, r, c); } // 覆盖左下角区域 if (r > xm && c <= ym) cover(xm + 1, y1, x2, ym, r, c); else { board[xm + 1][ym] = s; // 覆盖其余三个区域 if (r <= xm && c <= ym) cover(x1, y1, xm, ym, xm + 1, ym); if (r <= xm && c > ym) cover(x1, ym + 1, xm, y2, xm + 1, ym); cover(xm + 1, y1, x2, ym, xm + 1, ym); if (r > xm && c > ym) cover(xm + 1, ym + 1, x2, y2, r, c); } // 覆盖右下角区域 if (r > xm && c > ym) cover(xm + 1, ym + 1, x2, y2, r, c); else { board[xm + 1][ym + 1] = s; // 覆盖其余三个区域 if (r <= xm && c <= ym) cover(x1, y1, xm, ym, xm + 1, ym + 1); if (r <= xm && c > ym) cover(x1, ym + 1, xm, y2, xm + 1, ym + 1); if (r > xm && c <= ym) cover(xm + 1, y1, x2, ym, xm + 1, ym + 1); cover(xm + 1, ym + 1, x2, y2, xm + 1, ym + 1); } } // 初始化棋盘 void init_board(int n, int r, int c) { memset(board, -1, sizeof(board)); board[r][c] = 0; // 空白位置 tile = 1; } // 绘制棋盘 void draw_board(CDC* pDC, int n, int x, int y, int w) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (board[i][j] == -1) pDC->FillSolidRect(x + j * w, y + i * w, w, w, RGB(255, 255, 255)); // 白色 else if (board[i][j] == 0) pDC->FillSolidRect(x + j * w, y + i * w, w, w, RGB(0, 0, 0)); // 黑色 else pDC->FillSolidRect(x + j * w, y + i * w, w, w, RGB(255, 0, 0)); // 其他颜色 } } } // 覆盖棋盘 void cover_board(int n, int r, int c) { init_board(n, r, c); cover(0, 0, n - 1, n - 1, r, c); } // 棋盘覆盖操作 void CMyDlg::OnBnClickedButton1() { int n = 8; // 棋盘大小 int r = rand() % n, c = rand() % n; // 随机取一个空白位置 cover_board(n, r, c); // 覆盖棋盘 CClientDC dc(this); draw_board(&dc, n, 10, 50, 30); // 绘制棋盘 } ``` 在上述代码中,`cover()`函数使用递归算法实现棋盘的覆盖,`init_board()`函数用于初始化棋盘,`draw_board()`函数用于绘制棋盘。在点击按钮时,调用`cover_board()`函数进行棋盘覆盖,然后调用`draw_board()`函数绘制棋盘。运行程序后,每次点击按钮都会生成一个随机的空白位置,并用L型骨牌覆盖棋盘。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值