Leetcode困难笔记 37.解数独

解数独,回溯法即可。从左到右,从上到下,检查每种数字是否可以放进格子,如果可以就放入,一直搜索直到解数独完成。

class Solution{
public:
    bool finish = false;
    bool check(vector<vector<char> >& a,int i,int j,char k)
    {
        for(int p = 0; p < 9; p++)
            if(a[p][j] == k || a[i][p] == k)
                return false;
        for(int p = i/3*3; p < i/3*3+3; p++)
            for(int q = j/3*3; q < j/3*3+3; q++)
                if(a[p][q] == k)
                    return false;
        return true;
    }
    void solve(vector<vector<char> >& board,int i = 0, int j = 0)
    {
        if( j == 9 )
            i++, j = 0;
        if( i == 9 )
        {
            finish = true;
            return;
        }
        if( board[i][j] == '.' )
        {
            for(char k = '1'; k <= '9'; k++) {
				if(check(board,i,j,k))
				{
					board[i][j] = k;
					solve(board,i,j+1);
					if(finish == true)
						return;
					board[i][j] = '.';
				}
			}
        }
        else
            solve(board,i,j+1);
    }
    void solveSudoku(vector<vector<char> >& board) {
        solve(board);
        finish = false;
    }
};

既然数据部分有了,就简单写个界面来调试一下。由于绘图内容简单,直接用windows.h里的函数来画,最终大概是这个样子。

在这里插入图片描述

记录一下核心代码,以后怕是不想用windows.h来写了。

原来的解数独面板是字符串的,在改动后做成纯数字面板,遇到无效数独会异常退出,所以需要加个有效数独的检测。

游戏类,所有部件都放在这里了。

class SudokuGame
{
public:
	SudokuGame();
	~SudokuGame();
	void drawBoard(HDC hdc);
	void drawDigit(HDC hdc,int num,int i,int j);
	void drawData(HDC hdc);
    void clearData(HDC hdc);
    void calculateAndPrint(HDC hdc);
	void lButtonDownEvent(HWND& hwnd,HDC hdc);
	void keyDownEvent(HDC hdc,int input);
	bool waitInput();
	void showHelpWindow();
	void setWindowTop(HWND hwnd);
private:
    void drawCurBase(HDC hdc);
    void drawLine(HDC hdc,int x,int y,int target_x,int target_y);
    void drawCurLine(HDC hdc);
	void clearCurLine(HDC hdc);
    POINT clickpToInputp(POINT);
    bool PointInBoard(POINT);
	void initFont(HDC hdc);
	int data[9][9];
	int block_size;
	int board_x_offset, board_y_offset; //棋盘左上角的偏移量
	int digit_x_offset, digit_y_offset; //格子里数字的偏移量
	int sum_x_offset, sum_y_offset;
	int cur_line_length;
	bool can_input, is_top;
	HFONT hfont;
    POINT click_point, input_point;
	SudokuSolve* solver;
};

构造函数和析构函数,负责变量的初始化。

#define COLOR_BOARD_BK RGB(219,210,106)
#define COLOR_BOARD_LINE RGB(0,0,0)

SudokuGame::SudokuGame()
: block_size(60), can_input(false), is_top(false), cur_line_length(block_size/3*2),
  digit_x_offset(20), digit_y_offset(10),
  board_x_offset(20), board_y_offset(30),
  sum_x_offset(digit_x_offset+board_x_offset),
  sum_y_offset(digit_y_offset+board_y_offset)
{
	solver = new SudokuSolve();
	memset(data,0,sizeof(data));
}

SudokuGame::~SudokuGame()
{
	delete solver;
}

画线函数

void SudokuGame::drawLine(HDC hdc,int x,int y,int target_x,int target_y)
{
    MoveToEx(hdc,x,y,0);
    LineTo(hdc,target_x,target_y);
}

绘制棋盘格子

void SudokuGame::drawBoard(HDC hdc)
{
    initFont(hdc);
    SetBkColor(hdc,COLOR_BOARD_BK);
	int width = block_size*9, height = block_size*9;

	//画棋盘
	int left = board_x_offset, right = width+board_x_offset;
	int top = board_y_offset, bottom = height+board_y_offset;
	RECT rect = { left,top,right,bottom };
	FillRect(hdc,&rect,CreateSolidBrush(COLOR_BOARD_BK));

	//画横竖线
	for(int i = top; i <= bottom; i += block_size)
        drawLine(hdc,left,i,right,i);
	for(int i = left; i <= right; i += block_size)
        drawLine(hdc,i,top,i,bottom);

	//画粗分隔线
	HPEN pen = CreatePen(0,3,RGB(0,0,0));
	HPEN oldpen = (HPEN)SelectObject(hdc,pen);
	for(int i = top; i <= bottom; i += block_size*3)
        drawLine(hdc,left,i,right,i);
	for(int i = left; i <= right; i += block_size*3)
        drawLine(hdc,i,top,i,bottom);

	SelectObject(hdc,oldpen);
}

绘制数字部分

void SudokuGame::drawDigit(HDC hdc,int num,int i,int j)
{
	int digit_x = i*block_size+sum_x_offset;
	int digit_y = j*block_size+sum_y_offset;
	if( num >= 10 || num < 0 || i < 0 || i >= 9 || j < 0 || j >= 9 ) return;
    char output[2];
    output[0] = num + '0';
    output[1] = '\0';
	if( num )
		TextOut(hdc,digit_x,digit_y,output,1);
	else
		TextOut(hdc,digit_x,digit_y," ",1);
}

void SudokuGame::drawData(HDC hdc)
{
	for(int i = 0; i < 9; i++)
	for(int j = 0; j < 9; j++)
		drawDigit(hdc,data[i][j],i,j);
}

WinMain部分

SudokuGame* game;

//WinMain函数中
game = new SudokuGame();
hdc = GetDC(hwnd);
game->drawBoard(hdc);

字体部分

void SudokuGame::initFont(HDC hdc)
{
	hfont = CreateFont( 40, 0,
						  0, 0,
						  FW_NORMAL,
						  0,0,0,
						  GB2312_CHARSET,
						  OUT_CHARACTER_PRECIS,
						  CLIP_DEFAULT_PRECIS,
						  DEFAULT_QUALITY,
						  DEFAULT_PITCH |FF_DONTCARE,
						  "sudokuFont"
						 );
    SelectObject(hdc,hfont);
}

回调函数部分,省略了最大最小化重绘的代码。

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
	case WM_LBUTTONDOWN:
        game->lButtonDownEvent(hwnd,hdc);
		break;
	case WM_RBUTTONDOWN:
	    game->clearData(hdc);
		break;
	case WM_KEYDOWN:
		if( GetAsyncKeyState('T') )
		    game->setWindowTop(hwnd);
        else if( game->waitInput() )
            game->keyDownEvent(hdc,wParam);
		break;
	case WM_DESTROY:
		PostQuitMessage (0);
		break;
	default:
		return DefWindowProc (hwnd, message, wParam, lParam);
    }

回调函数响应函数

POINT SudokuGame::clickpToInputp(POINT p)
{
    POINT res = { p.x-board_x_offset, p.y-board_y_offset };
    res.x  = res.x >= 0 ? res.x / block_size : -1;
    res.y  = res.y >= 0 ? res.y / block_size : -1;
    return res;
}

void SudokuGame::lButtonDownEvent(HWND& hwnd,HDC hdc)
{
    if( can_input )
		clearCurLine(hdc);
	GetCursorPos(&click_point);
	ScreenToClient(hwnd,&click_point);
	input_point = clickpToInputp(click_point);
	drawCurLine(hdc);
	can_input = true;
}
void SudokuGame::keyDownEvent(HDC hdc,int input)
{
    if( input < 48 || input > 57) return;
	clearCurLine(hdc);
	if( input_point.x == -1 || input_point.y == -1 ) return;
    drawDigit(hdc,input-48,input_point.x,input_point.y);
    data[input_point.x][input_point.y] = input-48;
}

主要的处理代码就这么多,剩下一些没什么大影响的菜单部分。

本以为做个调试窗口能快速搞定的,但写了好久还只完成了基础的功能,代码也有很多需要修改的部分,还要多学习呢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值