puzzle(1051)暗码回路

目录

〇,暗码回路

一,问题建模

二,策略一:统计入度

三,策略二:去除孤点

四,策略三:箭头数字边界

五,432度分化

1,把射线化成线段

2,4度分化、3度分化

3,2度分化

4,新增实时校验

5,完整代码

六,1度分化

七,哈密顿圈判定

1,核心思路

2,代码1(432度分化)

3,代码2(1度分化)

八,DFS

1,代码3(1度分化)

2,代码4(DFS)

九,DEBUG

1,问题定位

2,打布丁方式

3,代码4(更新后的DFS)

4,可视化优化

十,再次DEBUG

1,测试用例

2,运行结果

3,定位结论

十一,完整代码(分离式)

1,代码0(可变部分)

2,代码1(432度分化)

3,代码2(1度分化)

4,代码3(DFS)

5,测试用例123

6,测试结果

 十二,完整代码(非分离式)

1,测试用例12345

2,测试结果

3,剪枝校验、测试用例6

4,定位结论

5,性能优化1

6,测试用例7、性能优化2

7,优化后完整代码

十三,数字识别

1,方法一:手搓

2,方法二:用AI自动识别

3,测试用例8、9、10

十四,最终版本

1,测试用例

2,定位结果

3,修复方案


〇,暗码回路

参考最强大脑第13季

一,问题建模

把格子分成四种,我们要做的就是把MAYBE的格子分成WALL或者EMPTY


enum class GridType :  int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	map<int, map<int, int>>num;
	map<int, map<int, int>>dir;
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■";
				else if (v[i][j] == GridType::EMPTY)cout << "□";
				else cout << "?  ";
				cout << " ";
			}
			cout << endl << endl;
		}
	}
};


int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	board.show();
	return 0;
}

输出:

二,策略一:统计入度

统计每个格子被指向的次数,如果为0,则一定是EMPTY

这是最开始执行的一次性策略。


enum class GridType :  int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	map<int, map<int, int>>num;
	map<int, map<int, int>>dir;
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "?  ";
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

void opt1(Board& board)
{
	map<int, map<int, int>>d;
	for (auto p : board.num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int dir = board.dir[r][c];
			for (int i = 0; i < 11; i++) {
				r += dr[dir], c += dc[dir];
				if (!board.inBoard(r, c))break;
				d[r][c]++;
			}
		}
	}
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j]==0) {
				board.v[i][j] = GridType::EMPTY;
			}
		}
	}
}

int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	opt1(board);
	board.show();
	return 0;
}

输出:

三,策略二:去除孤点

这里细分出2种去除孤点的策略,即opt2和opt3,都是可以多次使用的策略。


enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	map<int, map<int, int>>num;
	map<int, map<int, int>>dir;
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r,int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
};



//统计每个格子被指向的次数,如果为0,则一定是EMPTY
void opt1(Board& board)
{
	map<int, map<int, int>>d;
	for (auto p : board.num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int dir = board.dir[r][c];
			for (int i = 0; i < 11; i++) {
				r += dr[dir], c += dc[dir];
				if (!board.inBoard(r, c))break;
				d[r][c]++;
			}
		}
	}
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 0) {
				board.v[i][j] = GridType::EMPTY;
			}
		}
	}
}

//如果MAYBE格子旁边的MAYBE+EMPTY少于2个,则一定是WALL
bool opt2(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n < 2) {
					board.v[i][j] = GridType::WALL;
					ans = true;
				}
			}
		}
	}
	return ans;
}

//如果EMPTY格子旁边的MAYBE+EMPTY刚好2个,则旁边的一定是EMPTY
bool opt3(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n == 2) {
					for (auto par : nb) {
						if (board.v[par.first][par.second] == GridType::MAYBE) {
							board.v[par.first][par.second] = GridType::EMPTY;
							ans = true;
						}
					}					
				}
			}
		}
	}
	return ans;
}

int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	opt1(board);
	while (opt2(board) || opt3(board));
	board.show();
	return 0;
}

输出:

四,策略三:箭头数字边界

这里细分出2种边界场景,即opt4和opt5,都是可以多次使用的策略。


enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	map<int, map<int, int>>num;
	map<int, map<int, int>>dir;
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r,int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
};


//统计每个格子被指向的次数,如果为0,则一定是EMPTY
map<int, map<int, int>> opt1(Board& board)
{
	map<int, map<int, int>>d;
	for (auto p : board.num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int dir = board.dir[r][c];
			for (int i = 0; i < 11; i++) {
				r += dr[dir], c += dc[dir];
				if (!board.inBoard(r, c))break;
				d[r][c]++;
			}
		}
	}
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 0) {
				board.v[i][j] = GridType::EMPTY;
			}
		}
	}
	return d;
}

//如果MAYBE格子旁边的MAYBE+EMPTY少于2个,则一定是WALL
bool opt2(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n < 2) {
					board.v[i][j] = GridType::WALL;
					ans = true;
				}
			}
		}
	}
	return ans;
}

//如果EMPTY格子旁边的MAYBE+EMPTY刚好2个,则旁边的一定是EMPTY
bool opt3(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n == 2) {
					for (auto par : nb) {
						if (board.v[par.first][par.second] == GridType::MAYBE) {
							board.v[par.first][par.second] = GridType::EMPTY;
							ans = true;
						}
					}					
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字等于该方向WALL数量,则剩下的MAYBE都是EMPTY
bool opt4(Board& board)
{
	bool ans = false;
	for (auto p : board.num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int dir = board.dir[r][c];
			int num = 0;
			for (int i = 0; i < 11; i++) {
				r += dr[dir], c += dc[dir];
				if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL)num++;
			}
			r = p.first, c = par.first;
			if (num == board.num[r][c]) {
				for (int i = 0; i < 11; i++) {
					r += dr[dir], c += dc[dir];
					if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::EMPTY;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字等于该方向WALL+MAYBE数量,则剩下的MAYBE都是WALL
bool opt5(Board& board)
{
	bool ans = false;
	for (auto p : board.num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int dir = board.dir[r][c];
			int num = 0;
			for (int i = 0; i < 11; i++) {
				r += dr[dir], c += dc[dir];
				if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)num++;
			}
			r = p.first, c = par.first;
			if (num == board.num[r][c]) {
				for (int i = 0; i < 11; i++) {
					r += dr[dir], c += dc[dir];
					if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	map<int, map<int, int>>d = opt1(board);
	while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
	board.show();
	return 0;
}

输出:

五,432度分化

1,把射线化成线段

对于2→???1→??这种情况,可以分成2个线段:1→???和 1→??

这样,我们就需要给每个射线加一个长度,变成射线。

同时,我们对长度缩减的线段进行更新,把数量调至该线段(而不是该射线)上的WALL数量。

2,4度分化、3度分化

对入度为4的节点分类讨论,分化成多个待求解的局面。

再对入度为3的节点分类讨论,分化成多个待求解的局面。

到这一步,剩下的MAYBE节点都是入度为1或者2的了。

3,2度分化

假设剩下的a个MAYBE节点中,2度节点中有x个从MAYBE变成WALL,1度节点中有y个从MAYBE变成WALL,已经有b个WALL,c个EMPTY

那么,最终的EMPTY总数就是a-x-y+c,这个数必须是偶数,所以x+y的奇偶性必须和a+c的奇偶性相同。

假设这b个WALL中,4度到1度的数量分别是d4 d3 d2 d1,那么b=d4+d3+d2+d1

假设所有更新后的数字格总数为s,那么4 * d4 + 3 * d3 + 2 * d2 + d1 + 2 * x + y = s

综上可得:

x%2 = (a + c + s + d3 + d1)%2
y = s-(4 * d4 + 3 * d3 + 2 * d2 + d1) - x * 2

4,新增实时校验

在分化的过程中,不断的利用opt2到opt4去刷新,可能出现新的情况,即箭头数字边界被突破。

所以,基于opt4和opt5,提出check4和check5,实时判定无解情况。

5,完整代码


enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r,int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 11; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}
	
};


//统计每个格子被指向的次数,如果为0,则一定是EMPTY
map<int, map<int, int>> opt1(Board& board)
{
	map<int, map<int, int>>d;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				d[r][c]++;
			}
		}
	}
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 0) {
				board.v[i][j] = GridType::EMPTY;
			}
		}
	}
	return d;
}

//如果MAYBE格子旁边的MAYBE+EMPTY少于2个,则一定是WALL
bool opt2(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n < 2) {
					board.v[i][j] = GridType::WALL;
					ans = true;
				}
			}
		}
	}
	return ans;
}

//如果EMPTY格子旁边的MAYBE+EMPTY刚好2个,则旁边的一定是EMPTY
bool opt3(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n == 2) {
					for (auto par : nb) {
						if (board.v[par.first][par.second] == GridType::MAYBE) {
							board.v[par.first][par.second] = GridType::EMPTY;
							ans = true;
						}
					}					
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字等于该方向WALL数量,则剩下的MAYBE都是EMPTY
bool opt4(Board& board)
{
	bool ans = false;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum == num[r][c]) {
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					//if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::EMPTY;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字小于该方向WALL数量,则无解
bool check4(Board& board)
{
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum > num[r][c]) {
				return false;
			}
		}
	}
	return true;
}

//如果箭头上的数字等于该方向WALL+MAYBE数量,则剩下的MAYBE都是WALL
bool opt5(Board& board)
{
	bool ans = false;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)anum++;
			}
			r = p.first, c = par.first;
			if (anum == num[r][c]) {
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					//if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字大于该方向WALL+MAYBE数量,则无解
bool check5(Board& board)
{
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				return false;
			}
		}
	}
	return true;
}

bool check(Board& board)
{
	return check4(board) && check5(board);
}

vector<Board> killDx(Board& board, int r, int c)
{
	vector<Board> vb;
	board.v[r][c]= GridType::WALL;
	vb.push_back(board);
	board.v[r][c] = GridType::EMPTY;
	vb.push_back(board);
	return vb;
}
vector<Board> killDx(Board& board, map<int, map<int, int>> &d, int target)
{
	vector<Board> vb;
	vb.push_back(board);
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == target) {
				vector<Board> vb2;
				for (auto board : vb) {
					auto v = killDx(board, i, j);
					for (auto board : v) {
						vb2.push_back(board);
					}
				}
				vb = vb2;
			}
		}
	}
	return vb;
}

void killD2Last(Board& board, map<int, map<int, int>> &d, int r, int c)
{
	// 根据奇偶性直接确定board.v[i][j]是WALL还是EMPTY
	int ac = 0, d3d1 = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE || board.v[i][j] == GridType::EMPTY)ac++;
			else {
				if (d[i][j] == 3 || d[i][j] == 1)d3d1++;
			}
		}
	}
	if ((ac + d3d1 + numSum) % 2) {
		board.v[r][c] = GridType::WALL;
	}
	else {
		board.v[r][c] = GridType::EMPTY;
	}
}
vector<Board> killD2(Board& board, map<int, map<int, int>> &d)
{
	vector<Board> vb;
	vb.push_back(board);
	bool flag = true;
	int r = 0, c = 0;
	for (int i = 0; i < board.r; i++){
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 2) {
				if (flag) {
					flag = false;
					r = i, c = j;
					continue;
				}
				vector<Board> vb2;
				for (auto board : vb) {
					auto v = killDx(board, i, j);
					for (auto board : v) {
						if (!check(board))continue;
						vb2.push_back(board);
					}
				}
				vb = vb2;
			}
		}
		vector<Board> vb2;
		for (auto &board : vb) {
			while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
			if (!check(board))continue;
			vb2.push_back(board);
		}
		vb = vb2;
	}
	vector<Board> vb2;
	for (auto &board : vb) {
		killD2Last(board, d, r, c);
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		vb2.push_back(board);
	}
	return vb2;
}


int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	map<int, map<int, int>>d = opt1(board);
	while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
	board.show();
	vector<Board> vb4 = killDx(board, d, 4);
	cout << endl << vb4.size() << endl;
	vector<Board>vb3;
	for (auto &board : vb4) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		auto vb = killDx(board, d, 3);
		for (auto board : vb) {
			vb3.push_back(board);
		}
	}
	cout << vb3.size() << endl;
	vector<Board>vb2;
	for (auto &board : vb3) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		auto vb = killD2(board, d);
		for (auto board : vb) {
			vb2.push_back(board);
		}
	}
	cout << vb2.size() << endl;
	return 0;
}

输出:7860

数量还在可控范围内

六,1度分化

其实,到了这一步,所有的线段都已经没有交点了。

也就是说,所有的线段去选点都是正交的。当然,逻辑上还是会互相影响的,随时可以用opt2 opt3 opt4 opt5去优化然后用check去校验。

分化完之后,使用奇偶性检查:所有EMPTY格子中,奇格子的总数和偶格子的总数必须相等,否则无解。


#define GetAllCombina Combina::getAllCombina//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
#define Fforeach VectorOpt::fforeach//生成枚举序号



int allAnsNum = 0;

vector<Board> killD1(Board& board, map<int, map<int, int>> &d)
{
	//正交分组
	vector<vector<pair<int, int>>> groups;
	vector<int>groupSums;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				groupSums.push_back(num[r][c] - anum);
				vector<pair<int, int>>v;
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					if (board.v[r][c] == GridType::MAYBE)v.push_back({ r,c });
				}
				groups.push_back(v);
			}
		}
	}
	//计算每组的所有方案和对应的奇偶差分
	vector<vector<vector<pair<int, int>>>> pickLoc;
	vector<vector<int>>difs;
	for (int i = 0; i < groups.size(); i++) {
		vector<vector<pair<int, int>>> ans = GetAllCombina(groups[i], groupSums[i], true);
		pickLoc.push_back(ans);
		vector<int> dif;
		for (auto ansi : ans) {
			int difi = 0;
			for (auto loc : ansi) {
				if ((loc.first + loc.second) % 2)difi++;
				else difi--;
			}
			dif.push_back(difi);
		}
		difs.push_back(dif);
	}
	//计算目标差分
	int targetDif = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				if ((i + j) % 2)targetDif++;
				else targetDif--;
			}
		}
	}
	targetDif = -targetDif;
	//枚举所有满足目标差分的组合方案
	int ansNum = 0;
	vector<vector<int>> ids = Fforeach(pickLoc);
	for (auto vid : ids) {
		int dif = 0;
		for (int i = 0; i < vid.size(); i++) {
			dif += difs[i][vid[i]];
		}
		if (dif == targetDif) {
			ansNum++;
		}
	}
	allAnsNum += ansNum;
	cout << ansNum << " ";
	return {};
}

int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	map<int, map<int, int>>d = opt1(board);
	while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
	board.show();
	vector<Board> vb4 = killDx(board, d, 4);
	cout << endl << vb4.size() << endl;
	vector<Board>vb3;
	for (auto &board : vb4) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		auto vb = killDx(board, d, 3);
		for (auto board : vb) {
			vb3.push_back(board);
		}
	}
	cout << vb3.size() << endl;
	vector<Board>vb2;
	for (auto &board : vb3) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		auto vb = killD2(board, d);
		for (auto board : vb) {
			vb2.push_back(board);
		}
	}
	vector<Board>vb1;
	int x = 0;
	for (auto &board : vb2) {
		cout << ++x << " ";
		auto vb = killD1(board, d);
		for (auto board : vb) {
			vb1.push_back(board);
		}
	}
	cout << vb1.size() << endl;
	cout << allAnsNum << endl;
	/*
		for (auto board : vb1) {
		board.show();
		cout << endl << endl;
	}
	*/
	return 0;
}

输出:47263820

还在可以接受的数据量范围,照着这个思路继续优化即可。

七,哈密顿圈判定

1,核心思路

(1)添加折线类BrokenLines,记录哈密顿圈的片段信息。

(2)添加哈密顿圈判定类HamiltonCheck,基于折线信息更新节点的度,从而推理出更多信息。

(3)添加Board的输入输出,从而实现从任意位置切割,前面的用release跑完存到本地,后面的用debug读本地之后继续跑,便于调试。

2,代码1(432度分化)


enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r,int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto &vi : v) {
			vi.resize(c);
			for (auto &x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 11; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}
	
};



void killD2Last(Board& board, map<int, map<int, int>> &d, int r, int c)
{
	// 根据奇偶性直接确定board.v[i][j]是WALL还是EMPTY
	int ac = 0, d3d1 = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE || board.v[i][j] == GridType::EMPTY)ac++;
			else if (board.v[i][j] == GridType::WALL){
				if (d[i][j] == 3 || d[i][j] == 1)d3d1++;
			}
		}
	}
	if ((ac + d3d1 + numSum) % 2) {
		board.v[r][c] = GridType::WALL;
	}
	else {
		board.v[r][c] = GridType::EMPTY;
	}
}
vector<Board> killD2(Board& board, map<int, map<int, int>> &d)
{
	vector<Board> vb;
	vb.push_back(board);
	bool flag = true;
	int r = 0, c = -1;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 2) {
				if (flag) {
					flag = false;
					r = i, c = j;
					continue;
				}
				vector<Board> vb2;
				for (auto board : vb) {
					auto v = killDx(board, i, j);
					for (auto board : v) {
						if (!check(board))continue;
						vb2.push_back(board);
					}
				}
				vb = vb2;
			}
		}
		vector<Board> vb2;
		for (auto &board : vb) {
			while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
			if (!check(board))continue;
			vb2.push_back(board);
		}
		vb = vb2;
	}
	vector<Board> vb2;
	for (auto &board : vb) {
		if(c>-1)killD2Last(board, d, r, c);
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		vb2.push_back(board);
	}
	return vb2;
}

//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b && m.size() > 2) {
				//cout << "error! do not make circle!" << a << " " << b << endl;
				return false;
			}
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board &board_) :board(board_)
	{
	}
	bool updateOpt(bool &isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						bool flag1 = !brokenLines.isMid(id2);
						if (flag1 || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					isUpdate = true;
				}
			}
		}
		return true;
	}
	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({id, board.calId(i, j)})== brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3() || opt4(board) || opt5(board)) {
			if (!updateLine()) {
				return false;
			}
		}
		return check4(board) && check5(board);
	}
};

int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	map<int, map<int, int>>d = opt1(board);
	while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
	board.show();
	vector<Board> vb4 = killDx(board, d, 4);
	cout << endl << vb4.size() << endl;
	vector<Board>vb3;
	for (auto &board : vb4) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		auto vb = killDx(board, d, 3);
		for (auto board : vb) {
			vb3.push_back(board);
		}
	}
	cout << vb3.size() << endl;
	vector<Board>vb2;
	for (auto &board : vb3) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		auto vb = killD2(board, d);
		for (auto board : vb) {
			if (!HamiltonCheck(board).check())continue;
			vb2.push_back(board);
		}
	}
	freopen("D:/ans_vb2.txt", "w", stdout);
	cout << vb2.size() << endl;
	for (auto board : vb2)board.outPut();
	return 0;
}

运行结果:

在2度分化之后加了判定过滤,2度分化之后的情况数从7860降到了268

3,代码2(1度分化)


#define GetAllCombina Combina::getAllCombina//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
#define Fforeach VectorOpt::fforeach//生成枚举序号

enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board(){}
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto &vi : v) {
			vi.resize(c);
			for (auto &x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 11; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};



//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b && m.size() > 2) {
				//cout << "error! do not make circle!" << a << " " << b << endl;
				return false;
			}
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board &board_) :board(board_)
	{
	}
	bool updateOpt(bool &isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						bool flag1 = !brokenLines.isMid(id2);
						if (flag1 || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					isUpdate = true;
				}
			}
		}
		return true;
	}
	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({id, board.calId(i, j)})== brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3() || opt4(board) || opt5(board)) {
			if (!updateLine()) {
				return false;
			}
		}
		return check4(board) && check5(board);
	}
};

vector<Board> killD1(Board& board)
{
	//正交分组
	vector<vector<pair<int, int>>> groups;
	vector<int>groupSums;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				groupSums.push_back(num[r][c] - anum);
				vector<pair<int, int>>v;
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					if (board.v[r][c] == GridType::MAYBE)v.push_back({ r,c });
				}
				groups.push_back(v);
			}
		}
	}
	//计算每组的所有方案和对应的奇偶差分
	vector<vector<vector<pair<int, int>>>> pickLoc;
	vector<vector<int>>difs;
	for (int i = 0; i < groups.size(); i++) {
		vector<vector<pair<int, int>>> ans = GetAllCombina(groups[i], groupSums[i], true);
		pickLoc.push_back(ans);
		vector<int> dif;
		for (auto ansi : ans) {
			int difi = 0;
			for (auto loc : ansi) {
				if ((loc.first + loc.second) % 2)difi++;
				else difi--;
			}
			dif.push_back(difi);
		}
		difs.push_back(dif);
	}
	//计算目标差分
	int targetDif = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				if ((i + j) % 2)targetDif++;
				else targetDif--;
			}
		}
	}
	targetDif = -targetDif;
	//枚举所有满足目标差分的组合方案
	vector<vector<int>> ids = Fforeach(pickLoc);
	vector<Board> ans;
	static int x = 0;
	cout << "开始枚举" << ++x << " ";
	for (auto vid : ids) {
		int dif = 0;
		for (int i = 0; i < vid.size(); i++) {
			dif += difs[i][vid[i]];
		}
		if (dif == targetDif) {
			Board board2 = board;
			for (int i = 0; i < vid.size(); i++) {
				for (auto par : pickLoc[i][vid[i]]) {
					board2.v[par.first][par.second] = GridType::WALL;
				}
			}
			ans.push_back(board2);
		}
	}
	return ans;
}

int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	freopen("D:/ans_vb2.txt", "r", stdin);
	vector<Board>vb2;
	int siz;
	cin >> siz;
	vb2.resize(siz);
	for (auto &board : vb2)board.input();

	vector<Board>vb1;
	for (auto &board : vb2) {
		auto vb = killD1(board);
		for (auto board : vb) {
			if (!HamiltonCheck(board).check())continue;
			vb1.push_back(board);
		}
		cout << vb1.size() << endl;
	}
	cout << vb1.size() << endl;
	for (auto board : vb1) {
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE)board.v[i][j] = GridType::EMPTY;
				if (board.v[i][j] == GridType::NUM)board.v[i][j] = GridType::WALL;
			}
		}
		board.show();
		cout << endl << endl;
	}
	return 0;
}

在1度分化之后再次加判定过滤,从之前的47263820种情况降到了234种。

八,DFS

把1度分化的代码2再次拆成代码3和代码4,把求解出的234种情况都存下来,便于调试新增的DFS算法。

1,代码3(1度分化)


#define GetAllCombina Combina::getAllCombina//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
#define Fforeach VectorOpt::fforeach//生成枚举序号

enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board() {}
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto &vi : v) {
			vi.resize(c);
			for (auto &x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 11; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};



//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b && m.size() > 2) {
				//cout << "error! do not make circle!" << a << " " << b << endl;
				return false;
			}
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board &board_) :board(board_)
	{
	}
	bool updateOpt(bool &isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						bool flag1 = !brokenLines.isMid(id2);
						if (flag1 || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					isUpdate = true;
				}
			}
		}
		return true;
	}
	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({ id, board.calId(i, j) }) == brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3() || opt4(board) || opt5(board)) {
			if (!updateLine()) {
				return false;
			}
		}
		return check4(board) && check5(board);
	}
};

vector<Board> killD1(Board& board)
{
	//正交分组
	vector<vector<pair<int, int>>> groups;
	vector<int>groupSums;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				groupSums.push_back(num[r][c] - anum);
				vector<pair<int, int>>v;
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					if (board.v[r][c] == GridType::MAYBE)v.push_back({ r,c });
				}
				groups.push_back(v);
			}
		}
	}
	//计算每组的所有方案和对应的奇偶差分
	vector<vector<vector<pair<int, int>>>> pickLoc;
	vector<vector<int>>difs;
	for (int i = 0; i < groups.size(); i++) {
		vector<vector<pair<int, int>>> ans = GetAllCombina(groups[i], groupSums[i], true);
		pickLoc.push_back(ans);
		vector<int> dif;
		for (auto ansi : ans) {
			int difi = 0;
			for (auto loc : ansi) {
				if ((loc.first + loc.second) % 2)difi++;
				else difi--;
			}
			dif.push_back(difi);
		}
		difs.push_back(dif);
	}
	//计算目标差分
	int targetDif = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				if ((i + j) % 2)targetDif++;
				else targetDif--;
			}
		}
	}
	targetDif = -targetDif;
	//枚举所有满足目标差分的组合方案
	vector<vector<int>> ids = Fforeach(pickLoc);
	vector<Board> ans;
	static int x = 0;
	cout << "开始枚举" << ++x << " ";
	for (auto vid : ids) {
		int dif = 0;
		for (int i = 0; i < vid.size(); i++) {
			dif += difs[i][vid[i]];
		}
		if (dif == targetDif) {
			Board board2 = board;
			for (int i = 0; i < vid.size(); i++) {
				for (auto par : pickLoc[i][vid[i]]) {
					board2.v[par.first][par.second] = GridType::WALL;
				}
			}
			
			ans.push_back(board2);
		}
	}
	return ans;
}
int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	freopen("D:/ans_vb2.txt", "r", stdin);
	vector<Board>vb2;
	int siz;
	cin >> siz;
	vb2.resize(siz);
	for (auto &board : vb2)board.input();

	vector<Board>vb1;
	for (auto &board : vb2) {
		auto vb = killD1(board);
		for (auto board : vb) {
			if (!HamiltonCheck(board).check())continue;
			vb1.push_back(board);
		}
		cout << vb1.size() << endl;
	}
	freopen("D:/ans_vb1.txt", "w", stdout);
	cout << vb1.size() << endl;
	for (auto board : vb1) {
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE)board.v[i][j] = GridType::EMPTY;
				if (board.v[i][j] == GridType::NUM)board.v[i][j] = GridType::WALL;
			}
		}
		board.outPut();
	}
	return 0;
}

2,代码4(DFS)


enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board() {}
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}

	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto &vi : v) {
			vi.resize(c);
			for (auto &x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 11; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};


//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b && m.size() > 2) {
				//cout << "error! do not make circle!" << a << " " << b << endl;
				return false;
			}
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board &board_) :board(board_)
	{
	}
	bool updateOpt(bool &isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						bool flag1 = !brokenLines.isMid(id2);
						if (flag1 || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					isUpdate = true;
				}
			}
		}
		return true;
	}
	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({ id, board.calId(i, j) }) == brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3()) {
			if (!updateLine()) {
				return false;
			}
		}
		return true;
	}
};

bool dfs(HamiltonCheck &hc, int num, int id)
{
	int r = hc.board.r, c = hc.board.c;
	if (id >= r * c) {
		if (hc.brokenLines.m.size() == 2) {
			hc.board.show();
			cout << "all lines:" << endl;
			for (auto par : hc.brokenLines.s) {
				if (par.first < par.second) {
					cout << par.first << " " << par.second << "   ";
				}
			}
			return true;
		}
		return false;
	}
	int i = id / c, j = id % c;
	if (hc.brokenLines.isMid(id) || hc.board.v[i][j]== GridType::WALL)return dfs(hc, num, id + 1);
	vector<pair<int, int>> nb;
	for (int k = 1; k <= 2; k++) {
		int i2 = i + dr[k], j2 = j + dc[k];
		if (hc.board.inBoard(i2,j2) && hc.board.v[i2][j2] == GridType::EMPTY && !hc.brokenLines.isMid(hc.board.calId(i2, j2))){
			if (hc.brokenLines.s.find({ id, hc.board.calId(i2, j2) }) == hc.brokenLines.s.end()) {
				nb.push_back({ i2,j2 });
			}
		}
	}
	if (hc.brokenLines.isEnd(id)) {
		//再选1个
		for (auto par : nb) {
			HamiltonCheck hc2 = hc;
			if(hc2.brokenLines.update(id, hc.board.calId(par.first,par.second)) && dfs(hc2, num, id + 1))return true;
		}
	}
	else {
		//选2个
		if (nb.size() != 2)return false;
		for (auto par : nb) {
			if (!hc.brokenLines.update(id, hc.board.calId(par.first, par.second)))return false;
		}
		if (dfs(hc, num, id + 1))return true;
	}
	return false;
}
bool dfs(HamiltonCheck hc) //只有EMPTY和WALL,判断是否存在经过所有EMPTY的哈密顿圈
{
	int num = 0;
	for (int i = 0; i < hc.board.r; i++) {
		for (int j = 0; j < hc.board.c; j++) {
			if (hc.board.v[i][j] == GridType::EMPTY) {
				num++;
			}
		}
	}
	return dfs(hc,num,0);
}

int main()
{
	vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);
	freopen("D:/ans_vb1.txt", "r", stdin);
	vector<Board>vb1;
	int siz;
	cin >> siz;
	vb1.resize(siz);
	for (auto &board : vb1) {
		board.input();
		if (dfs(board)) {
			cout << "success!!!!!!!!!" << endl;
			//board.show();
		}
		else {
			cout << "fail" << endl;
		}
		//board.show();
		//cout << endl;
	}
	return 0;
}

输出:没有答案

还有BUG!!!

九,DEBUG

1,问题定位

用这一关来调试:

依次运行代码1,代码3,代码4,每个都把main函数里面改成:

	vector<NumGrid>n{
		{0,1,1,3},
		{0,5,2,3},
		{2,1,0,3},
		{2,4,1,0},
		{4,5,1,2},
		{5,0,2,0}
	};
	Board board(6, 6, n);

输出也是无答案。

简单定界一下,发现是代码4有问题,因为代码4的输入是:

1
6 6 
1 1 2 2 1 1 
2 2 2 2 2 2 
2 1 2 2 1 2 
2 2 2 2 2 2 
1 2 2 2 2 1 
1 2 2 2 2 1 

输出是无答案,而实际上是有由2组成的哈密顿圈的:

定位结论:

BrokenLines的update函数有问题,会把圈圈记作一条边(相当于二重边),从而影响了isMid的准确性。

2,打布丁方式

添加全局变量numInCircle,当进入DFS阶段,也就是最后阶段,启用numInCircle,直接判断当前的环是不是哈密顿圈。

相应的,DFS的结束条件hc.brokenLines.m.size() == 2需要改成hc.brokenLines.m.size() == 0

3,代码4(更新后的DFS)


#define GetAllCombina Combina::getAllCombina//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
#define Fforeach VectorOpt::fforeach//生成枚举序号

enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board() {}
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}

	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 11; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};

int numInCircle = INT_MAX;
//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b) {
				if (m.size() > 2) {
					//cout << "error! do not make circle!" << a << " " << b << endl;
					return false;
				}
				if (numInCircle != INT_MAX) {
					if (s.size() == numInCircle * 2 - 2) {
						m.erase(a);
						m.erase(b);
						s.insert({ a,b });
						s.insert({ b,a });
						return true;
					}
					return false;
				}
			}			
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board& board_) :board(board_)
	{
	}
	bool updateOpt(bool& isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						bool flag1 = !brokenLines.isMid(id2);
						if (flag1 || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					isUpdate = true;
				}
			}
		}
		return true;
	}
	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({ id, board.calId(i, j) }) == brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3()) {
			if (!updateLine()) {
				return false;
			}
		}
		return true;
	}
};

bool dfs(HamiltonCheck& hc, int num, int id)
{
	int r = hc.board.r, c = hc.board.c;
	if (id >= r * c) {
		if (hc.brokenLines.m.size() == 0) {
			hc.board.show();
			cout << "all lines:" << endl;
			for (auto par : hc.brokenLines.s) {
				if (par.first < par.second) {
					cout << par.first << " " << par.second << "   ";
				}
			}
			return true;
		}
		return false;
	}
	int i = id / c, j = id % c;
	if (hc.brokenLines.isMid(id) || hc.board.v[i][j] == GridType::WALL)return dfs(hc, num, id + 1);
	vector<pair<int, int>> nb;
	for (int k = 1; k <= 2; k++) {
		int i2 = i + dr[k], j2 = j + dc[k];
		if (hc.board.inBoard(i2, j2) && hc.board.v[i2][j2] == GridType::EMPTY && !hc.brokenLines.isMid(hc.board.calId(i2, j2))) {
			if (hc.brokenLines.s.find({ id, hc.board.calId(i2, j2) }) == hc.brokenLines.s.end()) {
				nb.push_back({ i2,j2 });
			}
		}
	}
	if (hc.brokenLines.isEnd(id)) {
		//再选1个
		for (auto par : nb) {
			HamiltonCheck hc2 = hc;
			if (hc2.brokenLines.update(id, hc.board.calId(par.first, par.second)) && dfs(hc2, num, id + 1))return true;
		}
	}
	else {
		//选2个
		if (nb.size() != 2)return false;
		for (auto par : nb) {
			if (!hc.brokenLines.update(id, hc.board.calId(par.first, par.second)))return false;
		}
		if (dfs(hc, num, id + 1))return true;
	}
	return false;
}
bool dfs(HamiltonCheck hc) //只有EMPTY和WALL,判断是否存在经过所有EMPTY的哈密顿圈
{
	int num = 0;
	for (int i = 0; i < hc.board.r; i++) {
		for (int j = 0; j < hc.board.c; j++) {
			if (hc.board.v[i][j] == GridType::EMPTY) {
				num++;
			}
		}
	}
	numInCircle = num;
	return dfs(hc, num, 0);
}

int main()
{
	vector<NumGrid>n{
	{0,1,1,3},
	{0,5,2,3},
	{2,1,0,3},
	{2,4,1,0},
	{4,5,1,2},
	{5,0,2,0}
	};
	Board board(6, 6, n);
	freopen("D:/ans_vb1.txt", "r", stdin);
	vector<Board>vb1;
	int siz;
	cin >> siz;
	vb1.resize(siz);
	for (auto& board : vb1) {
		board.input();
		if (dfs(board)) {
			cout << "success!!!!!!!!!" << endl;
			//board.show();
		}
		else {
			cout << "fail" << endl;
		}
		//board.show();
		//cout << endl;
	}
	return 0;
}

输出:

经过验证,这确实是正确答案。

4,可视化优化



struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board() {}
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	void showWithLine(set<pair<int, int>>s)
	{
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::WALL)cout << "■";
				else cout << "□";
				if (s.find({ calId(i,j),calId(i,j + 1) }) != s.end())cout << "-";
				else cout << " ";
			}
			cout << endl;
			for (int j = 0; j < c; j++) {
				if (s.find({ calId(i,j),calId(i+1,j) }) != s.end())cout << "|  ";
				else cout << "   ";
			}
			cout << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}

	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 11; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};


bool dfs(HamiltonCheck& hc, int num, int id)
{
	int r = hc.board.r, c = hc.board.c;
	if (id >= r * c) {
		if (hc.brokenLines.m.size() == 0) {
			hc.board.showWithLine(hc.brokenLines.s);
			return true;
		}
		return false;
	}
	int i = id / c, j = id % c;
	if (hc.brokenLines.isMid(id) || hc.board.v[i][j] == GridType::WALL)return dfs(hc, num, id + 1);
	vector<pair<int, int>> nb;
	for (int k = 1; k <= 2; k++) {
		int i2 = i + dr[k], j2 = j + dc[k];
		if (hc.board.inBoard(i2, j2) && hc.board.v[i2][j2] == GridType::EMPTY && !hc.brokenLines.isMid(hc.board.calId(i2, j2))) {
			if (hc.brokenLines.s.find({ id, hc.board.calId(i2, j2) }) == hc.brokenLines.s.end()) {
				nb.push_back({ i2,j2 });
			}
		}
	}
	if (hc.brokenLines.isEnd(id)) {
		//再选1个
		for (auto par : nb) {
			HamiltonCheck hc2 = hc;
			if (hc2.brokenLines.update(id, hc.board.calId(par.first, par.second)) && dfs(hc2, num, id + 1))return true;
		}
	}
	else {
		//选2个
		if (nb.size() != 2)return false;
		for (auto par : nb) {
			if (!hc.brokenLines.update(id, hc.board.calId(par.first, par.second)))return false;
		}
		if (dfs(hc, num, id + 1))return true;
	}
	return false;
}
bool dfs(HamiltonCheck hc) //只有EMPTY和WALL,判断是否存在经过所有EMPTY的哈密顿圈
{
	int num = 0;
	for (int i = 0; i < hc.board.r; i++) {
		for (int j = 0; j < hc.board.c; j++) {
			if (hc.board.v[i][j] == GridType::EMPTY) {
				num++;
			}
		}
	}
	numInCircle = num;
	return dfs(hc, num, 0);
}

int main()
{
	vector<NumGrid>n{
	{0,1,1,3},
	{0,5,2,3},
	{2,1,0,3},
	{2,4,1,0},
	{4,5,1,2},
	{5,0,2,0}
	};
	Board board(6, 6, n);
	freopen("D:/ans_vb1.txt", "r", stdin);
	vector<Board>vb1;
	int siz;
	cin >> siz;
	vb1.resize(siz);
	for (auto& board : vb1) {
		board.input();
		if (dfs(board)) {
			cout << "success!!!!!!!!!" << endl;
		}
		else {
			cout << "fail" << endl;
		}
	}
	return 0;
}

输出:

十,再次DEBUG

1,测试用例

vector<NumGrid>n{
{0,1,3,1},
{1,0,2,1},
{1,4,1,2},
{1,6,1,3},
{2,3,0,2},
{2,4,1,2},
{2,9,1,1},
{3,5,0,3},
{3,8,2,2},
{3,9,1,0},
{4,2,1,0},
{4,5,0,0},
{4,6,3,2},
{5,0,1,0},
{6,8,2,3},
{6,9,2,3},
{8,3,0,3},
{9,2,2,1},
{9,10,3,3},
{10,1,2,0},
{10,2,1,1}
};
Board board(12, 12, n);

2,运行结果

超时、无解

3,定位结论

问题1:

2 3 14 15构成了环,卡死了

修复方案:

修改BrokenLines的update函数

问题2:

HamiltonCheck的updateOpt函数中,碰到“如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY”,即opt3的场景,那么会更新brokenLines不会更新board,造成状态异常,次数再调用opt2会触发错误判断。

修复方案:

修改HamiltonCheck的updateOpt函数

十一,完整代码(分离式)

1,代码0(可变部分)

	vector<NumGrid>n{
	{0,1,1,3},
	{0,5,2,3},
	{2,1,0,3},
	{2,4,1,0},
	{4,5,1,2},
	{5,0,2,0}
	};
	Board board(6, 6, n);

这部分需要手段更新。

2,代码1(432度分化)


enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 100; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};


//统计每个格子被指向的次数,如果为0,则一定是EMPTY
map<int, map<int, int>> opt1(Board& board)
{
	map<int, map<int, int>>d;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				d[r][c]++;
			}
		}
	}
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 0) {
				board.v[i][j] = GridType::EMPTY;
			}
		}
	}
	return d;
}

//如果MAYBE格子旁边的MAYBE+EMPTY少于2个,则一定是WALL
bool opt2(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n < 2) {
					board.v[i][j] = GridType::WALL;
					ans = true;
				}
			}
		}
	}
	return ans;
}

//如果EMPTY格子旁边的MAYBE+EMPTY刚好2个,则旁边的一定是EMPTY
bool opt3(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n == 2) {
					for (auto par : nb) {
						if (board.v[par.first][par.second] == GridType::MAYBE) {
							board.v[par.first][par.second] = GridType::EMPTY;
							ans = true;
						}
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字等于该方向WALL数量,则剩下的MAYBE都是EMPTY
bool opt4(Board& board)
{
	bool ans = false;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum == num[r][c]) {
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					//if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::EMPTY;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字小于该方向WALL数量,则无解
bool check4(Board& board)
{
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum > num[r][c]) {
				return false;
			}
		}
	}
	return true;
}

//如果箭头上的数字等于该方向WALL+MAYBE数量,则剩下的MAYBE都是WALL
bool opt5(Board& board)
{
	bool ans = false;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)anum++;
			}
			r = p.first, c = par.first;
			if (anum == num[r][c]) {
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					//if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字大于该方向WALL+MAYBE数量,则无解
bool check5(Board& board)
{
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				return false;
			}
		}
	}
	return true;
}

bool check(Board& board)
{
	return check4(board) && check5(board);
}

vector<Board> killDx(Board& board, int r, int c)
{
	vector<Board> vb;
	board.v[r][c] = GridType::WALL;
	vb.push_back(board);
	board.v[r][c] = GridType::EMPTY;
	vb.push_back(board);
	return vb;
}
vector<Board> killDx(Board& board, map<int, map<int, int>>& d, int target)
{
	vector<Board> vb;
	vb.push_back(board);
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == target) {
				vector<Board> vb2;
				for (auto board : vb) {
					auto v = killDx(board, i, j);
					for (auto board : v) {
						vb2.push_back(board);
					}
				}
				vb = vb2;
			}
		}
	}
	return vb;
}

void killD2Last(Board& board, map<int, map<int, int>>& d, int r, int c)
{
	// 根据奇偶性直接确定board.v[i][j]是WALL还是EMPTY
	int ac = 0, d3d1 = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE || board.v[i][j] == GridType::EMPTY)ac++;
			else if (board.v[i][j] == GridType::WALL) {
				if (d[i][j] == 3 || d[i][j] == 1)d3d1++;
			}
		}
	}
	if ((ac + d3d1 + numSum) % 2) {
		board.v[r][c] = GridType::WALL;
	}
	else {
		board.v[r][c] = GridType::EMPTY;
	}
}
vector<Board> killD2(Board& board, map<int, map<int, int>>& d)
{
	vector<Board> vb;
	vb.push_back(board);
	bool flag = true;
	int r = 0, c = -1;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 2) {
				if (flag) {
					flag = false;
					r = i, c = j;
					continue;
				}
				vector<Board> vb2;
				for (auto board : vb) {
					auto v = killDx(board, i, j);
					for (auto board : v) {
						if (!check(board))continue;
						vb2.push_back(board);
					}
				}
				vb = vb2;
			}
		}
		vector<Board> vb2;
		for (auto& board : vb) {
			while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
			if (!check(board))continue;
			vb2.push_back(board);
		}
		vb = vb2;
	}
	vector<Board> vb2;
	for (auto& board : vb) {
		if (c > -1)killD2Last(board, d, r, c);
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		vb2.push_back(board);
	}
	return vb2;
}

int numInCircle = INT_MAX;
//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b) {
				if (m.size() > 2) {
					//cout << "error! do not make circle!" << a << " " << b << endl;
					return false;
				}
				if (numInCircle != INT_MAX) {
					if (s.size() == numInCircle * 2 - 2) {
						m.erase(a);
						m.erase(b);
						s.insert({ a,b });
						s.insert({ b,a });
						return true;
					}
					return false;
				}
			}
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board& board_) :board(board_)
	{
	}
	bool updateOpt(bool& isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						if (!brokenLines.isMid(id2) || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					board.v[ids[0] / board.c][ids[0] % board.c] = GridType::EMPTY;
					board.v[ids[1] / board.c][ids[1] % board.c] = GridType::EMPTY;
					isUpdate = true;
				}
			}
		}
		return true;
	}
	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({ id, board.calId(i, j) }) == brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3() || opt4(board) || opt5(board)) {
			if (!updateLine()) {
				return false;
			}
		}
		return check4(board) && check5(board);
	}
};

int main()
{
	//////////填入可变部分
	numInCircle--;
	map<int, map<int, int>>d = opt1(board);
	while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
	board.show();
	vector<Board> vb4 = killDx(board, d, 4);
	cout << endl << vb4.size() << endl;
	vector<Board>vb3;
	for (auto& board : vb4) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		auto vb = killDx(board, d, 3);
		for (auto board : vb) {
			vb3.push_back(board);
		}
	}
	cout << vb3.size() << endl;
	vector<Board>vb2;
	for (auto& board : vb3) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		auto vb = killD2(board, d);
		for (auto board : vb) {
			if (!HamiltonCheck(board).check())continue;
			vb2.push_back(board);
		}
	}
	freopen("D:/ans_vb2.txt", "w", stdout);
	cout << vb2.size() << endl;
	for (auto board : vb2)board.outPut();
	return 0;
}

3,代码2(1度分化)


class Combina
{
public:
	//在一个列表中选出若干个数,得到所有排列,按照id的字典序来排序
	template<typename T>
	static vector<vector<T>> getAllPermutation(const vector<T>& v, int n, bool flag)// flag表示是否去掉重复的排列
	{
		vector<vector<T>>ans;
		if (n <= 0 || n > v.size())return ans;
		vector<T>tmp(n);
		vector<int>m(v.size(), 0);
		dfs(n, 1, v, m, tmp, ans);
		if (flag) {
			sort(ans.begin(), ans.end());
			ans.erase(unique(ans.begin(), ans.end()), ans.end());
		}
		return ans;
	}
	//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
	template<typename T>
	static vector<vector<T>> getAllCombina(const vector<T>& v, int n, bool flag)// flag=true表示不允许组合中有重复元素
	{
		vector<vector<T>>ans;
		if (n <= 0 || n > v.size())return ans;
		vector<T>tmp(n);
		vector<int>m(v.size(), 0);
		dfs2(n, 1, 0, v, m, tmp, ans);
		if (flag) {
			sort(ans.begin(), ans.end());
			ans.erase(unique(ans.begin(), ans.end()), ans.end());
		}
		return ans;
	}
	//把s拆分成n个不同数的和, 返回所有排列
	static vector<vector<int>> intSplitA(int s, int n, const set<int>& m)//m给出了可选的所有数
	{
		vector<vector<int>>ans;
		if (n == 1) {
			if (m.find(s) == m.end())return ans;
			auto x = vector<vector<int>>(1, vector<int>(1, s));
			return x;
		}
		for (auto p : m) {
			set<int>m2 = m;
			m2.erase(p);
			vector<vector<int>>v = intSplitA(s - p, n - 1, m2);
			for (auto& vi : v) {
				vi.push_back(p);
				ans.push_back(vi);
			}
		}
		return ans;
	}
	//把s拆分成n个不同数的和, 返回所有组合
	static vector<vector<int>> intSplitC(int s, int n, set<int>& m)//m给出了可选的所有数
	{
		vector<vector<int>>ans;
		if (m.empty())return ans;
		if (n == 1) {
			if (m.find(s) == m.end())return ans;
			auto x = vector<vector<int>>(1, vector<int>(1, s));
			return x;
		}
		int p = *m.begin();
		m.erase(p);
		set<int> m2 = m;
		ans = intSplitC(s, n, m);
		vector<vector<int>>v = intSplitC(s - p, n - 1, m2);
		for (auto& vi : v) {
			vi.push_back(p);
			ans.push_back(vi);
		}
		return ans;
	}
private:
	//排列的dfs
	template<typename T>
	static void dfs(int n, int deep, const vector<T>& v, vector<int>& m, vector<T>& tmp, vector<vector<T>>& ans)
	{
		if (deep > n) {
			ans.push_back(tmp);
			return;
		}
		for (int i = 0; i < v.size(); i++) {
			if (m[i])continue;
			tmp[deep - 1] = v[i];
			m[i] = 1;
			dfs(n, deep + 1, v, m, tmp, ans);
			m[i] = 0;
		}
	}
	//组合的dfs
	template<typename T>
	static void dfs2(int n, int deep, int id, const vector<T>& v, vector<int>& m, vector<T>& tmp, vector<vector<T>>& ans)
	{
		if (deep > n) {
			ans.push_back(tmp);
			return;
		}
		if (id >= v.size())return;
		for (int i = id; i < v.size(); i++) {
			if (m[i])continue;
			tmp[deep - 1] = v[i];
			m[i] = 1;
			dfs2(n, deep + 1, i + 1, v, m, tmp, ans);
			m[i] = 0;
		}
	}
};

class VectorOpt
{
public:
	//收缩计数,把[1 4 4 1]变成canSort ? [(1,2)(4,2)] : [(1,1)(4,2)(1,1)]
	template<typename T>
	static vector<pair<T, int>> fshr(vector<T>v, bool canSort = true) //谨慎传引用
	{
		vector<pair<T, int>>ans;
		if (v.size() == 0)return ans;
		if (canSort)sort(v.begin(), v.end());
		int low = 0;
		for (int i = 1; i <= v.size(); i++) {
			if (i == v.size() || v[i] != v[i - 1]) {
				ans.push_back(make_pair(v[i - 1], i - low));
				low = i;
			}
		}
		return ans;
	}
	//平摊,把 [(1,2)(4,2)]变成[1 1 4 4]
	template<typename T>
	static vector<T> funshr(vector<pair<T, int>>& v)
	{
		vector<T>ans;
		for (auto p : v) {
			for (int i = 0; i < p.second; i++)ans.push_back(p.first);
		}
		return ans;
	}
	//生成枚举序号
	template<typename T>
	static vector<vector<int>> fforeach(const vector<vector<T>>& v)
	{
		int s = 1;
		for (auto& vi : v)s *= vi.size();
		vector<vector<int>> ans;
		while (s--)
		{
			vector<int> vt;
			int st = s;
			for (int j = v.size() - 1; j >= 0; j--)
			{
				vt.insert(vt.begin(), st % v[j].size());
				st /= v[j].size();
			}
			ans.push_back(vt);
		}
		return ans;
	}
	//根据各数字对应的替换列表,生成各种替代枚举
	template<typename T>
	static vector<vector<T>> changeMeiJu(const vector<T>& data, map<T, vector<T>>options)
	{
		vector<vector<T>> ans(1, data);
		int len = data.size();
		for (int i = 0; i < len; i++) {
			int s = ans.size();
			for (int j = 0; j < s; j++)for (auto op : options[data[i]]) {
				vector<T> x = ans[j];
				x[i] = op;
				ans.push_back(x);
			}
		}
		return ans;
	}
	//根据各id对应的替换列表,生成各种替代枚举
	template<typename T>
	static vector<T> subMeiJu(const T& data, int len, map<int, T>options)
	{
		vector<T> ans;
		ans.push_back(data);
		for (int i = 0; i < len; i++) {
			int s = ans.size();
			for (int j = 0; j < s; j++)for (auto op : options[i]) {
				T x = ans[j];
				x[i] = op;
				ans.push_back(x);
			}
		}
		return ans;
	}
	//拓展数据域,加上id
	template<typename T>
	static inline vector<pair<T, int>>expandWithId(const vector<T>& v)
	{
		vector<pair<T, int>>ans;
		ans.resize(v.size());
		for (int i = 0; i < v.size(); i++)ans[i].first = v[i], ans[i].second = i;
		return ans;
	}
	//给vector拓展,加上id并拓展成稳定排序
	template<typename T>
	static inline vector<pair<T, int>> sortWithId(const vector<T>& v)
	{
		vector<pair<T, int>>ans = expandWithId(v);
		sort(ans.begin(), ans.end(), cmpPair<T, int>);
		return ans;
	}
	//给vector拓展,加上id,但只按照原数据进行排序
	template<typename T>
	static inline vector<pair<T, int>> sortWithOrderMatch(const vector<T>& v)
	{
		vector<pair<T, int>>ans = expandWithId(v);
		sort(ans.begin(), ans.end(), cmpJustFirst<T, int>);
		return ans;
	}
	//排序后数组中的每个数的原ID,输入8 5 6 7,输出1 2 3 0,也可以直接求逆置换
	template<typename T>
	static inline vector<int> sortId(const vector<T>& v)
	{
		auto vp = sortWithId(v);
		vector<int>ans;
		transform(vp.begin(), vp.end(), back_inserter(ans), [](const pair<T, int>& p) {return p.second; });
		return ans;
	}
	//每个数在排序后的数组中的ID,输入8 5 6 7,输出3 0 1 2
	template<typename T>
	static inline vector<int> sortId2(const vector<T>& v)
	{
		return sortId(sortId(v));
	}
	//给v排序,v2按照同样的顺序调整,输入{1,3,2}{4,5,6},输出{1,2,3}{4,6,5}
	template<typename T, typename T2>
	static inline void sortExtend(vector<T>& v, vector<T2>& v2)
	{
		auto ids = sortId(v);
		auto v3 = v, v4 = v2;
		for (int i = 0; i < v.size(); i++)v[i] = v3[ids[i]], v2[i] = v4[ids[i]];
	}
	//输入2个数组,输入保证v2是v的重排列,输出映射数组ids,使得v2[i]=v[ids[i]]恒成立
	//输入{3,2,5,9,6,4},{5,3,2,4,6,9},输出{2,0,1,5,4,3}
	template<typename T>
	static inline vector<int>getMatchId(vector<T>& v, vector<T>& v2)
	{
		vector<int> id1 = sortId(v);
		vector<int> id2 = sortId2(v2);
		vector<int> ans;
		for (int i = 0; i < id1.size(); i++)ans.push_back(id1[id2[i]]);
		return ans;
	}
	//2个vector中寻找和为s的对,返回结果的每一行都是[id1,id2]
	template<typename T>
	static vector<vector<int>> findSum(vector<T>v1, vector<T>v2, T s)
	{
		vector<vector<int>>ans;
		int m = min((long long)(v1.size() * v2.size()), (long long)12345678);
		ans.reserve(m);
		vector<int>tmp(2);
		vector<int>v3 = sortId(v2);
		sort(v2.begin(), v2.end(), cmp<T>);
		for (int i = 0; i < v1.size(); i++)
		{
			auto it1 = lower_bound(v2.begin(), v2.end(), s - v1[i]);
			auto it2 = upper_bound(v2.begin(), v2.end(), s - v1[i]);
			tmp[0] = i;
			for (auto j = it1; j < it2; j++)
			{
				tmp[1] = v3[j - v2.begin()];
				ans.push_back(tmp);
			}
		}
		return ans;
	}
	//vector的字典序比较,v1<v2是true,v1>=v2是false
	template<typename T>
	static bool cmpVector(const vector<T>& v1, const vector<T>& v2)
	{
		for (int i = 0; i < v1.size() && i < v2.size(); i++)
		{
			if (v1[i] != v2[i])return v1[i] < v2[i];
		}
		return v1.size() < v2.size();
	}
	//vector的字典序排序
	template<typename T>
	static void sortVector(vector<vector<T>>& v)
	{
		sort(v.begin(), v.end(), cmpVector<T>);
	}
private:
	template<typename T>
	static inline bool cmp(T a, T b)
	{
		return a < b;
	}
	static inline bool cmp(string a, string b)
	{
		return a.length() < b.length();
	}
	template<typename T, typename T2>
	static inline bool cmpPair(pair<T, T2> x, pair<T, T2> y)
	{
		if (cmp(x.first, y.first))return true;
		if (cmp(y.first, x.first))return false;
		return cmp(x.second, y.second);
	}
	template<typename T, typename T2>
	static inline bool cmpJustFirst(pair<T, T2> x, pair<T, T2> y)
	{
		return cmp(x.first, y.first);
	}
};

#define GetAllCombina Combina::getAllCombina//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
#define Fforeach VectorOpt::fforeach//生成枚举序号

enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board() {}
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 100; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};

//如果箭头上的数字等于该方向WALL数量,则剩下的MAYBE都是EMPTY
bool opt4(Board& board)
{
	bool ans = false;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum == num[r][c]) {
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					//if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::EMPTY;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字小于该方向WALL数量,则无解
bool check4(Board& board)
{
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum > num[r][c]) {
				return false;
			}
		}
	}
	return true;
}

//如果箭头上的数字等于该方向WALL+MAYBE数量,则剩下的MAYBE都是WALL
bool opt5(Board& board)
{
	bool ans = false;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)anum++;
			}
			r = p.first, c = par.first;
			if (anum == num[r][c]) {
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					//if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字大于该方向WALL+MAYBE数量,则无解
bool check5(Board& board)
{
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				return false;
			}
		}
	}
	return true;
}

bool check(Board& board)
{
	return check4(board) && check5(board);
}

int numInCircle = INT_MAX;
//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b) {
				if (m.size() > 2) {
					//cout << "error! do not make circle!" << a << " " << b << endl;
					return false;
				}
				if (numInCircle != INT_MAX) {
					if (s.size() == numInCircle * 2 - 2) {
						m.erase(a);
						m.erase(b);
						s.insert({ a,b });
						s.insert({ b,a });
						return true;
					}
					return false;
				}
			}
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board& board_) :board(board_)
	{
	}
	bool updateOpt(bool& isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						if (!brokenLines.isMid(id2) || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					board.v[ids[0] / board.c][ids[0] % board.c] = GridType::EMPTY;
					board.v[ids[1] / board.c][ids[1] % board.c] = GridType::EMPTY;
					isUpdate = true;
				}
			}
		}
		return true;
	}

	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({ id, board.calId(i, j) }) == brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3() || opt4(board) || opt5(board)) {
			if (!updateLine()) {
				return false;
			}
		}
		return check4(board) && check5(board);
	}
};

vector<Board> killD1(Board& board)
{
	//正交分组
	vector<vector<pair<int, int>>> groups;
	vector<int>groupSums;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				groupSums.push_back(num[r][c] - anum);
				vector<pair<int, int>>v;
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					if (board.v[r][c] == GridType::MAYBE)v.push_back({ r,c });
				}
				groups.push_back(v);
			}
		}
	}
	//计算每组的所有方案和对应的奇偶差分
	vector<vector<vector<pair<int, int>>>> pickLoc;
	vector<vector<int>>difs;
	for (int i = 0; i < groups.size(); i++) {
		vector<vector<pair<int, int>>> ans = GetAllCombina(groups[i], groupSums[i], true);
		pickLoc.push_back(ans);
		vector<int> dif;
		for (auto ansi : ans) {
			int difi = 0;
			for (auto loc : ansi) {
				if ((loc.first + loc.second) % 2)difi++;
				else difi--;
			}
			dif.push_back(difi);
		}
		difs.push_back(dif);
	}
	//计算目标差分
	int targetDif = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				if ((i + j) % 2)targetDif++;
				else targetDif--;
			}
		}
	}
	targetDif = -targetDif;
	//枚举所有满足目标差分的组合方案
	vector<vector<int>> ids = Fforeach(pickLoc);
	vector<Board> ans;
	static int x = 0;
	cout << "开始枚举" << ++x << endl;
	for (auto vid : ids) {
		int dif = 0;
		for (int i = 0; i < vid.size(); i++) {
			dif += difs[i][vid[i]];
		}
		if (dif == targetDif) {
			Board board2 = board;
			for (int i = 0; i < vid.size(); i++) {
				for (auto par : pickLoc[i][vid[i]]) {
					board2.v[par.first][par.second] = GridType::WALL;
				}
			}

			ans.push_back(board2);
		}
	}
	return ans;
}
int main()
{
	//////////填入可变部分
	numInCircle--;
	freopen("D:/ans_vb2.txt", "r", stdin);
	vector<Board>vb2;
	int siz;
	cin >> siz;
	vb2.resize(siz);
	for (auto& board : vb2)board.input();

	vector<Board>vb1;
	for (auto& board : vb2) {
		auto vb = killD1(board);
		for (auto board : vb) {
			if (!HamiltonCheck(board).check())continue;
			vb1.push_back(board);
		}
		cout << vb1.size() << endl;
	}
	freopen("D:/ans_vb1.txt", "w", stdout);
	cout << vb1.size() << endl;
	for (auto board : vb1) {
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE)board.v[i][j] = GridType::EMPTY;
				if (board.v[i][j] == GridType::NUM)board.v[i][j] = GridType::WALL;
			}
		}
		board.outPut();
	}
	return 0;
}

4,代码3(DFS)


enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board() {}
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	void showWithLine(set<pair<int, int>>s)
	{
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::WALL)cout << "■";
				else cout << "□";
				if (s.find({ calId(i,j),calId(i,j + 1) }) != s.end())cout << "-";
				else cout << " ";
			}
			cout << endl;
			for (int j = 0; j < c; j++) {
				if (s.find({ calId(i,j),calId(i + 1,j) }) != s.end())cout << "|";
				else cout << " ";
				cout << " ";//里面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
			}
			cout << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}

	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 100; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};

int numInCircle = INT_MAX;
//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b) {
				if (m.size() > 2) {
					//cout << "error! do not make circle!" << a << " " << b << endl;
					return false;
				}
				if (numInCircle != INT_MAX) {
					if (s.size() == numInCircle * 2 - 2) {
						m.erase(a);
						m.erase(b);
						s.insert({ a,b });
						s.insert({ b,a });
						return true;
					}
					return false;
				}
			}
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board& board_) :board(board_)
	{
	}
	bool updateOpt(bool& isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						bool flag1 = !brokenLines.isMid(id2);
						if (flag1 || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					isUpdate = true;
				}
			}
		}
		return true;
	}
	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({ id, board.calId(i, j) }) == brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3()) {
			if (!updateLine()) {
				return false;
			}
		}
		return true;
	}
};

bool dfs(HamiltonCheck& hc, int num, int id)
{
	int r = hc.board.r, c = hc.board.c;
	if (id >= r * c) {
		if (hc.brokenLines.m.size() == 0) {
			hc.board.showWithLine(hc.brokenLines.s);
			return true;
		}
		return false;
	}
	int i = id / c, j = id % c;
	if (hc.brokenLines.isMid(id) || hc.board.v[i][j] == GridType::WALL)return dfs(hc, num, id + 1);
	vector<pair<int, int>> nb;
	for (int k = 1; k <= 2; k++) {
		int i2 = i + dr[k], j2 = j + dc[k];
		if (hc.board.inBoard(i2, j2) && hc.board.v[i2][j2] == GridType::EMPTY && !hc.brokenLines.isMid(hc.board.calId(i2, j2))) {
			if (hc.brokenLines.s.find({ id, hc.board.calId(i2, j2) }) == hc.brokenLines.s.end()) {
				nb.push_back({ i2,j2 });
			}
		}
	}
	if (hc.brokenLines.isEnd(id)) {
		//再选1个
		for (auto par : nb) {
			HamiltonCheck hc2 = hc;
			if (hc2.brokenLines.update(id, hc.board.calId(par.first, par.second)) && dfs(hc2, num, id + 1))return true;
		}
	}
	else {
		//选2个
		if (nb.size() != 2)return false;
		for (auto par : nb) {
			if (!hc.brokenLines.update(id, hc.board.calId(par.first, par.second)))return false;
		}
		if (dfs(hc, num, id + 1))return true;
	}
	return false;
}
bool dfs(HamiltonCheck hc) //只有EMPTY和WALL,判断是否存在经过所有EMPTY的哈密顿圈
{
	int num = 0;
	for (int i = 0; i < hc.board.r; i++) {
		for (int j = 0; j < hc.board.c; j++) {
			if (hc.board.v[i][j] == GridType::EMPTY) {
				num++;
			}
		}
	}
	numInCircle = num;
	return dfs(hc, num, 0);
}

int main()
{
	//////////填入可变部分
	freopen("D:/ans_vb1.txt", "r", stdin);
	vector<Board>vb1;
	int siz;
	cin >> siz;
	vb1.resize(siz);
	for (auto& board : vb1) {
		board.input();
		if (dfs(board)) {
			cout << "success!!!!!!!!!" << endl;
		}
		else {
			cout << "fail" << endl;
		}
	}
	return 0;
}

5,测试用例123

case1:

 

	vector<NumGrid>n{
	{0,1,1,3},
	{0,5,2,3},
	{2,1,0,3},
	{2,4,1,0},
	{4,5,1,2},
	{5,0,2,0}
	};
	Board board(6, 6, n);

case2:

vector<NumGrid>n{
		{0,1,1,2},
		{0,11,3,3},
		{1,3,2,1},
		{1,8,1,2},
		{2,4,1,1},
		{3,2,0,1},
		{3,10,4,2},
		{5,8,2,3},
		{6,8,1,3},
		{7,2,1,1},
		{9,8,1,3},
		{10,2,3,1},
		{10,10,3,0},
		{11,5,4,0},
		{11,6,2,1}
	};
	Board board(12, 12, n);

case3:

vector<NumGrid>n{
{0,1,3,1},
{1,0,2,1},
{1,4,1,2},
{1,6,1,3},
{2,3,0,2},
{2,4,1,2},
{2,9,1,1},
{3,5,0,3},
{3,8,2,2},
{3,9,1,0},
{4,2,1,0},
{4,5,0,0},
{4,6,3,2},
{5,0,1,0},
{6,8,2,3},
{6,9,2,3},
{8,3,0,3},
{9,2,2,1},
{9,10,3,3},
{10,1,2,0},
{10,2,1,1}
};
Board board(12, 12, n);

6,测试结果

case1通过

case2通过

case3失败,仍然输出无解

 十二,完整代码(非分离式)

1,测试用例12345

case1同上,case2同上,case3同上

case4:

    vector<NumGrid>n{
        {0,9,2,3},
        {1,7,1,3},
        {2,4,1,1},
        {3,4,1,1},
        {4,4,1,2},
        {5,11,1,0},
        {6,3,2,1},
        {6,5,1,3},
        {7,11,5,3},
        {8,9,1,0},
        {9,2,0,0},
        {9,9,1,3},
        {9,10,2,0},
        {11,2,5,1}
    };
    Board board(12, 12, n);

case5:

  

    vector<NumGrid>n{
        {0,3,1,1},
        {0,8,1,1},
        {2,1,1,0},
        {2,7,2,2},
        {3,9,1,3},
        {3,11,3,2},
        {4,0,4,1},
        {6,2,1,3},
        {6,3,1,0},
        {6,9,1,2},
        {6,10,1,2},
        {7,9,1,3},
        {7,10,1,3},
        {7,5,0,0},
        {8,2,1,0},
        {8,4,0,0},
        {8,7,1,0},
        {9,0,2,1},
        {9,7,1,3},
        {10,0,2,0},
        {11,10,1,3}
    };
    Board board(12, 12, n);

2,测试结果

case1通过

case2通过

case3失败,仍然输出无解

case4失败,内存崩溃

case5失败,仍然输出无解

3,剪枝校验、测试用例6

新增一种debug方法,输入答案,基于答案去定界哪一个过程把正确答案排除掉了。

case5加上ans就得到case6:

	// 可变部分开始
	vector<NumGrid>n{
		{0,3,1,1},
		{0,8,1,1},
		{2,1,1,0},
		{2,7,2,2},
		{3,9,1,3},
		{3,11,3,2},
		{4,0,4,1},
		{6,2,1,3},
		{6,3,1,0},
		{6,9,1,2},
		{6,10,1,2},
		{7,9,1,3},
		{7,10,1,3},
		{7,5,0,0},
		{8,2,1,0},
		{8,4,0,0},
		{8,7,1,0},
		{9,0,2,1},
		{9,7,1,3},
		{10,0,2,0},
		{11,10,1,3}
	};
	Board board(12, 12, n);
	//答案中所有WALL的坐标,不知道答案就不写这一行
	wallPosAns = { {0,9},{1,1},{3,3},{4,1},{4,6},{4,7},{4,11},{5,0},{6,0},{7,2},{9,9},{10,10},{10,11}, {11,11},{10,7},{11,0},{9,6}	}; 
	//可变部分结束

4,定位结论

问题1:

目标差分的计算有问题

问题2:

killD1输出的vector长度达到829万,内存超了

5,性能优化1

解决了这2个问题之后,case1到case6就全部通过了,但是case4还是比较慢。

显然,问题就出在killD1的829万数据量,虽然解决了内存问题,不用存下来,可以边产出边求解,但是求解时间还是比较长。

于是,我在这里面又插入了剪枝。

case4从89秒优化到1秒以内


bool check(Board board, vector<pair<int, int>>&pickWall)
{
	for (auto par : pickWall)board.v[par.first][par.second] = GridType::WALL;
	return HamiltonCheck(board).check();
}
bool killD1AndDFS(Board& board, set<pair<int, int>> &wallPosAns)
{
	//step1:正交分组
	vector<vector<pair<int, int>>> groups;//groups[i]是第i组有哪些MAYBE格子
	vector<int>groupSums;//groupSums[i]是第i组需要选出几个格子成为WALL
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				groupSums.push_back(num[r][c] - anum);
				vector<pair<int, int>>v;
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					if (board.v[r][c] == GridType::MAYBE)v.push_back({ r,c });
				}
				groups.push_back(v);
			}
		}
	}
	//step2:计算每组的所有方案和对应的奇偶差分
	vector<vector<vector<pair<int, int>>>> pickLoc;//pickLoc[i][j]是第i组的第j种选取方案中选了哪些格子成为WALL
	vector<vector<int>>difs;//difs[i][j]是第i组的第j种选取方案的奇偶差分
	for (int i = 0; i < groups.size(); i++) {
		vector<vector<pair<int, int>>> ans = GetAllCombina(groups[i], groupSums[i], true);
		vector<vector<pair<int, int>>> ans2;
		vector<int> dif;
		for (auto ansi : ans) {
			if (!check(board, ansi))continue;
			int difi = 0;
			for (auto loc : ansi) {
				if ((loc.first + loc.second) % 2)difi++;
				else difi--;
			}
			dif.push_back(difi);
			ans2.push_back(ansi);
		}
		pickLoc.push_back(ans2);
		difs.push_back(dif);
	}
	//step3:计算目标差分
	int targetDif = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY || board.v[i][j] == GridType::MAYBE) {
				if ((i + j) % 2)targetDif++;
				else targetDif--;
			}
		}
	}
	//step4:枚举所有满足目标差分的组合方案
	vector<vector<int>> ids = Fforeach(pickLoc);
	cout << "待枚举数量:" << ids.size() << endl;
	int x = 0;
	for (auto vid : ids) {
		//if (++x % 1000 == 0)cout << "枚举序号=" << x << "  ";
		int dif = 0;
		for (int i = 0; i < vid.size(); i++) {
			dif += difs[i][vid[i]];
		}
		if (dif != targetDif) {
			continue;
		}
		Board board2 = board;
		for (int i = 0; i < vid.size(); i++) {
			for (auto par : pickLoc[i][vid[i]]) {
				board2.v[par.first][par.second] = GridType::WALL;
			}
		}
		if (!checkBoard(board2, wallPosAns)) {
			continue;
		}
		if (!HamiltonCheck(board2).check())continue;
		for (int i = 0; i < board2.r; i++) {
			for (int j = 0; j < board2.c; j++) {
				if (board2.v[i][j] == GridType::MAYBE)board2.v[i][j] = GridType::EMPTY;
				if (board2.v[i][j] == GridType::NUM)board2.v[i][j] = GridType::WALL;
			}
		}
		if (dfs(board2)) {
			cout << "success!!!!!!!!!" << endl;
			return true;
		}
		else {
			cout << "fail" << endl;
		}
	}
	cout << endl;
	return false;
}

6,测试用例7、性能优化2

虽然case1到case6很快了,但是对于新的case7:

	vector<NumGrid>n{
		{0,0,3,  1   },
		{0,1,1,  2   },
		{0,2,3,  1   },
		{0,7,2,  2   },
		{1,6,1,  3   },
		{1,9,2,  2   },
		{2,9,1,  3   },
		{2,10,2,  2   },
		{3,9,3,   3  },
		{4,3,2,   2  },
		{4,5,2,   0  },
		{4,11,2,  2   },
		{5,5,1,    2 },
		{7,6,2,   2  },
		{7,10,2,  2   },
		{11,11,4,  3   },

	};
	Board board(12, 12, n);

还是很慢。

因为case7中,数字指向的格子很多,所以枚举数量很高,跑了11分钟,大概运行了60%的总进度,还没得到答案,内存崩溃了。

还需要继续剪枝。

优化思路:

(1)把原本的根据所有的1度的组进行正交组合,改成分成2大组,2个大组各自正交组合,根据差分进行精确匹配。

参考 力扣 454. 四数相加 II:给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

(2)2个大组各自再进行一次过滤,更新WALL,对于已经确定无解的情况,直接过滤掉

7,优化后完整代码

修复了2个BUG、新增了2个性能优化

#include <iostream>
#include <string>
#include <vector>
#include <time.h>
#include <functional>
#include <algorithm>
#include <vector>
#include <queue>
#include <numeric>
#include <map>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <array>
#include <functional>
#include <string.h>
#include <math.h>
#include <fstream>
#include <streambuf>
#include <stdio.h>
#include <mutex>
#include <unordered_map>
#include <iostream>
#include <vector>
#include <iomanip>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <functional>
#include <limits>
#include <climits>
#include <stdint.h>
#include <windows.h>

using namespace std;



class Combina
{
public:
	//在一个列表中选出若干个数,得到所有排列,按照id的字典序来排序
	template<typename T>
	static vector<vector<T>> getAllPermutation(const vector<T>& v, int n, bool flag)// flag表示是否去掉重复的排列
	{
		vector<vector<T>>ans;
		if (n <= 0 || n > v.size())return ans;
		vector<T>tmp(n);
		vector<int>m(v.size(), 0);
		dfs(n, 1, v, m, tmp, ans);
		if (flag) {
			sort(ans.begin(), ans.end());
			ans.erase(unique(ans.begin(), ans.end()), ans.end());
		}
		return ans;
	}
	//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
	template<typename T>
	static vector<vector<T>> getAllCombina(const vector<T>& v, int n, bool flag)// flag=true表示不允许组合中有重复元素
	{
		vector<vector<T>>ans;
		if (n <= 0 || n > v.size())return ans;
		vector<T>tmp(n);
		vector<int>m(v.size(), 0);
		dfs2(n, 1, 0, v, m, tmp, ans);
		if (flag) {
			sort(ans.begin(), ans.end());
			ans.erase(unique(ans.begin(), ans.end()), ans.end());
		}
		return ans;
	}
	//把s拆分成n个不同数的和, 返回所有排列
	static vector<vector<int>> intSplitA(int s, int n, const set<int>& m)//m给出了可选的所有数
	{
		vector<vector<int>>ans;
		if (n == 1) {
			if (m.find(s) == m.end())return ans;
			auto x = vector<vector<int>>(1, vector<int>(1, s));
			return x;
		}
		for (auto p : m) {
			set<int>m2 = m;
			m2.erase(p);
			vector<vector<int>>v = intSplitA(s - p, n - 1, m2);
			for (auto& vi : v) {
				vi.push_back(p);
				ans.push_back(vi);
			}
		}
		return ans;
	}
	//把s拆分成n个不同数的和, 返回所有组合
	static vector<vector<int>> intSplitC(int s, int n, set<int>& m)//m给出了可选的所有数
	{
		vector<vector<int>>ans;
		if (m.empty())return ans;
		if (n == 1) {
			if (m.find(s) == m.end())return ans;
			auto x = vector<vector<int>>(1, vector<int>(1, s));
			return x;
		}
		int p = *m.begin();
		m.erase(p);
		set<int> m2 = m;
		ans = intSplitC(s, n, m);
		vector<vector<int>>v = intSplitC(s - p, n - 1, m2);
		for (auto& vi : v) {
			vi.push_back(p);
			ans.push_back(vi);
		}
		return ans;
	}
private:
	//排列的dfs
	template<typename T>
	static void dfs(int n, int deep, const vector<T>& v, vector<int>& m, vector<T>& tmp, vector<vector<T>>& ans)
	{
		if (deep > n) {
			ans.push_back(tmp);
			return;
		}
		for (int i = 0; i < v.size(); i++) {
			if (m[i])continue;
			tmp[deep - 1] = v[i];
			m[i] = 1;
			dfs(n, deep + 1, v, m, tmp, ans);
			m[i] = 0;
		}
	}
	//组合的dfs
	template<typename T>
	static void dfs2(int n, int deep, int id, const vector<T>& v, vector<int>& m, vector<T>& tmp, vector<vector<T>>& ans)
	{
		if (deep > n) {
			ans.push_back(tmp);
			return;
		}
		if (id >= v.size())return;
		for (int i = id; i < v.size(); i++) {
			if (m[i])continue;
			tmp[deep - 1] = v[i];
			m[i] = 1;
			dfs2(n, deep + 1, i + 1, v, m, tmp, ans);
			m[i] = 0;
		}
	}
};

class VectorOpt
{
public:
	//收缩计数,把[1 4 4 1]变成canSort ? [(1,2)(4,2)] : [(1,1)(4,2)(1,1)]
	template<typename T>
	static vector<pair<T, int>> fshr(vector<T>v, bool canSort = true) //谨慎传引用
	{
		vector<pair<T, int>>ans;
		if (v.size() == 0)return ans;
		if (canSort)sort(v.begin(), v.end());
		int low = 0;
		for (int i = 1; i <= v.size(); i++) {
			if (i == v.size() || v[i] != v[i - 1]) {
				ans.push_back(make_pair(v[i - 1], i - low));
				low = i;
			}
		}
		return ans;
	}
	//平摊,把 [(1,2)(4,2)]变成[1 1 4 4]
	template<typename T>
	static vector<T> funshr(vector<pair<T, int>>& v)
	{
		vector<T>ans;
		for (auto p : v) {
			for (int i = 0; i < p.second; i++)ans.push_back(p.first);
		}
		return ans;
	}
	//生成枚举序号
	template<typename T>
	static vector<vector<int>> fforeach(const vector<vector<T>>& v)
	{
		int s = 1;
		for (auto& vi : v)s *= vi.size();
		vector<vector<int>> ans;
		while (s--)
		{
			vector<int> vt;
			int st = s;
			for (int j = v.size() - 1; j >= 0; j--)
			{
				vt.insert(vt.begin(), st % v[j].size());
				st /= v[j].size();
			}
			ans.push_back(vt);
		}
		return ans;
	}
	//根据各数字对应的替换列表,生成各种替代枚举
	template<typename T>
	static vector<vector<T>> changeMeiJu(const vector<T>& data, map<T, vector<T>>options)
	{
		vector<vector<T>> ans(1, data);
		int len = data.size();
		for (int i = 0; i < len; i++) {
			int s = ans.size();
			for (int j = 0; j < s; j++)for (auto op : options[data[i]]) {
				vector<T> x = ans[j];
				x[i] = op;
				ans.push_back(x);
			}
		}
		return ans;
	}
	//根据各id对应的替换列表,生成各种替代枚举
	template<typename T>
	static vector<T> subMeiJu(const T& data, int len, map<int, T>options)
	{
		vector<T> ans;
		ans.push_back(data);
		for (int i = 0; i < len; i++) {
			int s = ans.size();
			for (int j = 0; j < s; j++)for (auto op : options[i]) {
				T x = ans[j];
				x[i] = op;
				ans.push_back(x);
			}
		}
		return ans;
	}
	//拓展数据域,加上id
	template<typename T>
	static inline vector<pair<T, int>>expandWithId(const vector<T>& v)
	{
		vector<pair<T, int>>ans;
		ans.resize(v.size());
		for (int i = 0; i < v.size(); i++)ans[i].first = v[i], ans[i].second = i;
		return ans;
	}
	//给vector拓展,加上id并拓展成稳定排序
	template<typename T>
	static inline vector<pair<T, int>> sortWithId(const vector<T>& v)
	{
		vector<pair<T, int>>ans = expandWithId(v);
		sort(ans.begin(), ans.end(), cmpPair<T, int>);
		return ans;
	}
	//给vector拓展,加上id,但只按照原数据进行排序
	template<typename T>
	static inline vector<pair<T, int>> sortWithOrderMatch(const vector<T>& v)
	{
		vector<pair<T, int>>ans = expandWithId(v);
		sort(ans.begin(), ans.end(), cmpJustFirst<T, int>);
		return ans;
	}
	//排序后数组中的每个数的原ID,输入8 5 6 7,输出1 2 3 0,也可以直接求逆置换
	template<typename T>
	static inline vector<int> sortId(const vector<T>& v)
	{
		auto vp = sortWithId(v);
		vector<int>ans;
		transform(vp.begin(), vp.end(), back_inserter(ans), [](const pair<T, int>& p) {return p.second; });
		return ans;
	}
	//每个数在排序后的数组中的ID,输入8 5 6 7,输出3 0 1 2
	template<typename T>
	static inline vector<int> sortId2(const vector<T>& v)
	{
		return sortId(sortId(v));
	}
	//给v排序,v2按照同样的顺序调整,输入{1,3,2}{4,5,6},输出{1,2,3}{4,6,5}
	template<typename T, typename T2>
	static inline void sortExtend(vector<T>& v, vector<T2>& v2)
	{
		auto ids = sortId(v);
		auto v3 = v, v4 = v2;
		for (int i = 0; i < v.size(); i++)v[i] = v3[ids[i]], v2[i] = v4[ids[i]];
	}
	//输入2个数组,输入保证v2是v的重排列,输出映射数组ids,使得v2[i]=v[ids[i]]恒成立
	//输入{3,2,5,9,6,4},{5,3,2,4,6,9},输出{2,0,1,5,4,3}
	template<typename T>
	static inline vector<int>getMatchId(vector<T>& v, vector<T>& v2)
	{
		vector<int> id1 = sortId(v);
		vector<int> id2 = sortId2(v2);
		vector<int> ans;
		for (int i = 0; i < id1.size(); i++)ans.push_back(id1[id2[i]]);
		return ans;
	}
	//2个vector中寻找和为s的对,返回结果的每一行都是[id1,id2]
	template<typename T>
	static vector<vector<int>> findSum(vector<T>v1, vector<T>v2, T s)
	{
		vector<vector<int>>ans;
		int m = min((long long)(v1.size() * v2.size()), (long long)12345678);
		ans.reserve(m);
		vector<int>tmp(2);
		vector<int>v3 = sortId(v2);
		sort(v2.begin(), v2.end(), cmp<T>);
		for (int i = 0; i < v1.size(); i++)
		{
			auto it1 = lower_bound(v2.begin(), v2.end(), s - v1[i]);
			auto it2 = upper_bound(v2.begin(), v2.end(), s - v1[i]);
			tmp[0] = i;
			for (auto j = it1; j < it2; j++)
			{
				tmp[1] = v3[j - v2.begin()];
				ans.push_back(tmp);
			}
		}
		return ans;
	}
	//vector的字典序比较,v1<v2是true,v1>=v2是false
	template<typename T>
	static bool cmpVector(const vector<T>& v1, const vector<T>& v2)
	{
		for (int i = 0; i < v1.size() && i < v2.size(); i++)
		{
			if (v1[i] != v2[i])return v1[i] < v2[i];
		}
		return v1.size() < v2.size();
	}
	//vector的字典序排序
	template<typename T>
	static void sortVector(vector<vector<T>>& v)
	{
		sort(v.begin(), v.end(), cmpVector<T>);
	}
private:
	template<typename T>
	static inline bool cmp(T a, T b)
	{
		return a < b;
	}
	static inline bool cmp(string a, string b)
	{
		return a.length() < b.length();
	}
	template<typename T, typename T2>
	static inline bool cmpPair(pair<T, T2> x, pair<T, T2> y)
	{
		if (cmp(x.first, y.first))return true;
		if (cmp(y.first, x.first))return false;
		return cmp(x.second, y.second);
	}
	template<typename T, typename T2>
	static inline bool cmpJustFirst(pair<T, T2> x, pair<T, T2> y)
	{
		return cmp(x.first, y.first);
	}
};

#define GetAllCombina Combina::getAllCombina//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
#define Fforeach VectorOpt::fforeach//生成枚举序号

enum class GridType : int {
	NUM, WALL, EMPTY, MAYBE //MAYBE最后才能确定是WALL或者EMPTY
};

struct NumGrid {
	int r, c, num, dir;//0上 1右 2下 3左
};

vector<int>dr{ -1,0,1,0 };
vector<int>dc{ 0,1,0,-1 };

map<int, map<int, int>>num;
map<int, map<int, int>>dir;
map<int, map<int, int>>len; //每个箭头到指向方向的下一个同向箭头中间的格子数
int numSum = 0;

struct Board
{
	int r, c;
	vector<vector<GridType>>v;
	Board() {}
	Board(int r, int c, vector<NumGrid>& n)
	{
		this->r = r, this->c = c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& vii : vi)vii = GridType::MAYBE;
		}
		for (auto ni : n) {
			v[ni.r][ni.c] = GridType::NUM;
			num[ni.r][ni.c] = ni.num;
			dir[ni.r][ni.c] = ni.dir;
		}
		InitLen();
	}
	void show()
	{
		vector<string>s{ "↑","→","↓","←" };
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM) cout << num[i][j] << s[dir[i][j]];
				else if (v[i][j] == GridType::WALL)cout << "■ ";
				else if (v[i][j] == GridType::EMPTY)cout << "□ ";
				else cout << "? "; //问号后面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
				cout << " ";
			}
			cout << endl << endl;
		}
	}
	void showWithLine(set<pair<int, int>>s)
	{
		for (int i = 0; i < r; i++) {
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::WALL)cout << "■";
				else cout << "□";
				if (s.find({ calId(i,j),calId(i,j + 1) }) != s.end())cout << "-";
				else cout << " ";
			}
			cout << endl;
			for (int j = 0; j < c; j++) {
				if (s.find({ calId(i,j),calId(i + 1,j) }) != s.end())cout << "|";
				else cout << " ";
				cout << "  ";//里面是1个或者2个空格,不同的显示窗口参数不一样,需要灵活调整
			}
			cout << endl;
		}
	}
	bool inBoard(int r, int c)
	{
		return r >= 0 && c >= 0 && r < this->r && c < this->c;
	}
	vector<pair<int, int>> getNeighbor(int r, int c)
	{
		vector<pair<int, int>>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(r + dr[i], c + dc[i]))ans.push_back({ r + dr[i],c + dc[i] });
		}
		return ans;
	}
	int calId(int i, int j)
	{
		return i * c + j;
	}
	void outPut()
	{
		cout << r << " " << c << " ";
		for (auto vi : v) {
			for (auto x : vi)cout << int(x) << " ";
		}
	}
	void input()
	{
		int a;
		cin >> r >> c;
		v.resize(r);
		for (auto& vi : v) {
			vi.resize(c);
			for (auto& x : vi) {
				cin >> a;
				x = static_cast<GridType>(a);
			}
		}
	}
private:
	void InitLen(vector<pair<int, int>>vp, int dir)
	{
		numSum += num[vp[0].first][vp[0].second];
		for (int i = 0; i < vp.size() - 1; i++) {
			len[vp[i].first][vp[i].second] = abs(vp[i].first + vp[i].second - vp[i + 1].first - vp[i + 1].second) - 1;//不是非法数据就不会溢出
			num[vp[i].first][vp[i].second] -= num[vp[i + 1].first][vp[i + 1].second];
		}
		int id = vp.size() - 1;
		int r = vp[id].first, c = vp[id].second;
		for (int i = 0; i < 100; i++) {
			r += dr[dir], c += dc[dir];
			if (!inBoard(r, c))break;
			len[vp[id].first][vp[id].second]++;
		}
	}
	void InitLen()
	{
		vector<pair<int, int>>vp;
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = r - 1; i >= 0; i--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 0) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 0);
		}
		for (int j = 0; j < c; j++) {
			vp.clear();
			for (int i = 0; i < r; i++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 2) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 2);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = 0; j < c; j++) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 1) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 1);
		}
		for (int i = 0; i < r; i++) {
			vp.clear();
			for (int j = c - 1; j >= 0; j--) {
				if (v[i][j] == GridType::NUM && dir[i][j] == 3) {
					vp.push_back({ i,j });
				}
			}
			if (vp.size())InitLen(vp, 3);
		}
		return;
	}

};


//统计每个格子被指向的次数,如果为0,则一定是EMPTY
map<int, map<int, int>> opt1(Board& board)
{
	map<int, map<int, int>>d;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				d[r][c]++;
			}
		}
	}
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 0) {
				board.v[i][j] = GridType::EMPTY;
			}
		}
	}
	return d;
}

//如果MAYBE格子旁边的MAYBE+EMPTY少于2个,则一定是WALL
bool opt2(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n < 2) {
					board.v[i][j] = GridType::WALL;
					ans = true;
				}
			}
		}
	}
	return ans;
}

//如果EMPTY格子旁边的MAYBE+EMPTY刚好2个,则旁边的一定是EMPTY
bool opt3(Board& board)
{
	bool ans = false;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY) {
				int n = 0;
				vector<pair<int, int>> nb = board.getNeighbor(i, j);
				for (auto par : nb) {
					if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
				}
				if (n == 2) {
					for (auto par : nb) {
						if (board.v[par.first][par.second] == GridType::MAYBE) {
							board.v[par.first][par.second] = GridType::EMPTY;
							ans = true;
						}
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字等于该方向WALL数量,则剩下的MAYBE都是EMPTY
bool opt4(Board& board)
{
	bool ans = false;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum == num[r][c]) {
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					//if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::EMPTY;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字小于该方向WALL数量,则无解
bool check4(Board& board)
{
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum > num[r][c]) {
				return false;
			}
		}
	}
	return true;
}

//如果箭头上的数字等于该方向WALL+MAYBE数量,则剩下的MAYBE都是WALL
bool opt5(Board& board)
{
	bool ans = false;
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				//if (!board.inBoard(r, c))break;
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)anum++;
			}
			r = p.first, c = par.first;
			if (anum == num[r][c]) {
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					//if (!board.inBoard(r, c))break;
					if (board.v[r][c] == GridType::MAYBE) {
						board.v[r][c] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
	}
	return ans;
}

//如果箭头上的数字大于该方向WALL+MAYBE数量,则无解
bool check5(Board& board)
{
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL || board.v[r][c] == GridType::MAYBE)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				return false;
			}
		}
	}
	return true;
}

bool check(Board& board)
{
	return check4(board) && check5(board);
}

vector<Board> killDx(Board& board, int r, int c)
{
	vector<Board> vb;
	board.v[r][c] = GridType::WALL;
	vb.push_back(board);
	board.v[r][c] = GridType::EMPTY;
	vb.push_back(board);
	return vb;
}
vector<Board> killDx(Board& board, map<int, map<int, int>>& d, int target)
{
	vector<Board> vb;
	vb.push_back(board);
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == target) {
				vector<Board> vb2;
				for (auto board : vb) {
					auto v = killDx(board, i, j);
					for (auto board : v) {
						vb2.push_back(board);
					}
				}
				vb = vb2;
			}
		}
	}
	return vb;
}

void killD2Last(Board& board, map<int, map<int, int>>& d, int r, int c)
{
	// 根据奇偶性直接确定board.v[i][j]是WALL还是EMPTY
	int ac = 0, d3d1 = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE || board.v[i][j] == GridType::EMPTY)ac++;
			else if (board.v[i][j] == GridType::WALL) {
				if (d[i][j] == 3 || d[i][j] == 1)d3d1++;
			}
		}
	}
	if ((ac + d3d1 + numSum) % 2) {
		board.v[r][c] = GridType::WALL;
	}
	else {
		board.v[r][c] = GridType::EMPTY;
	}
}
vector<Board> killD2(Board& board, map<int, map<int, int>>& d)
{
	vector<Board> vb;
	vb.push_back(board);
	bool flag = true;
	int r = 0, c = -1;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::MAYBE && d[i][j] == 2) {
				if (flag) {
					flag = false;
					r = i, c = j;
					continue;
				}
				vector<Board> vb2;
				for (auto board : vb) {
					auto v = killDx(board, i, j);
					for (auto board : v) {
						if (!check(board))continue;
						vb2.push_back(board);
					}
				}
				vb = vb2;
			}
		}
		vector<Board> vb2;
		for (auto& board : vb) {
			while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
			if (!check(board))continue;
			vb2.push_back(board);
		}
		vb = vb2;
	}
	vector<Board> vb2;
	for (auto& board : vb) {
		if (c > -1)killD2Last(board, d, r, c);
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		vb2.push_back(board);
	}
	return vb2;
}

int numInCircle = INT_MAX;
//基于有限节点的无环多折线更新
struct BrokenLines
{
public:
	map<int, int> m; //若m[a]=b则m[b]=a,ab之间有一条折线
	set<pair<int, int>>s;
	bool update(int a, int b) //把ab连起来
	{
		if (s.find({ a,b }) != s.end())return true;
		if (isMid(a) || isMid(b)) {
			//cout << "error! do not make circle!" << a << " " << b << endl;
			return false;
		}
		bool hasA = endOrMid[a];
		bool hasB = endOrMid[b];
		if (hasA && hasB) {
			if (m[a] == b) {
				if (m.size() > 2) {
					//cout << "error! do not make circle!" << a << " " << b << endl;
					return false;
				}
				if (numInCircle != INT_MAX) {
					if (s.size() == numInCircle * 2 - 2) {
						m.erase(a);
						m.erase(b);
						s.insert({ a,b });
						s.insert({ b,a });
						return true;
					}
					return false;
				}
			}
			int c = m[a], d = m[b];
			m.erase(a);
			m.erase(b);
			m[c] = d, m[d] = c;
		}
		else if (hasA) {
			int c = m[a];
			m[c] = b, m[b] = c;
			m.erase(a);
		}
		else if (hasB) {
			int d = m[b];
			m[d] = a, m[a] = d;
			m.erase(b);
		}
		else {
			m[a] = b, m[b] = a;
		}
		endOrMid[a] = true;
		endOrMid[b] = true;
		s.insert({ a,b });
		s.insert({ b,a });
		return true;
	}
	bool isEndOrMid(int a)
	{
		return endOrMid[a];
	}
	bool isEnd(int a)
	{
		return (m.find(a) != m.end());
	}
	bool isMid(int a)
	{
		return (m.find(a) == m.end() && endOrMid[a]);
	}
	void show()
	{
		cout << "broken lines:";
		for (auto mi : m) {
			if (mi.first < mi.second)cout << mi.first << " " << mi.second << "   ";
		}
		cout << endl;
	}
private:
	map<int, bool> endOrMid; //是否是某条折线的端点或者内点
};

//基于有限信息的哈密顿圈判定
class HamiltonCheck
{
public:
	Board board;
	BrokenLines brokenLines;
public:
	HamiltonCheck(Board& board_) :board(board_)
	{
	}
	bool updateOpt(bool& isUpdate)
	{
		//EMPTY是需要经过的格子,MAYBE是可能需要经过的格子,其他是不经过的格子
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				int id = board.calId(i, j);
				if (board.v[i][j] != GridType::EMPTY || brokenLines.isMid(id)) {
					continue;
				}
				auto vb = board.getNeighbor(i, j);
				vector<int>ids;
				for (auto vi : vb) {
					if (board.v[vi.first][vi.second] == GridType::MAYBE || board.v[vi.first][vi.second] == GridType::EMPTY) {
						int id2 = board.calId(vi.first, vi.second);
						if (!brokenLines.isMid(id2) || brokenLines.s.find({ id,id2 }) != brokenLines.s.end()) {
							ids.push_back(id2);
						}
					}
				}
				if (ids.size() < 2) {
					return false;
				}
				if (ids.size() == 2) {
					if (!brokenLines.update(ids[0], id))return false;
					if (!brokenLines.update(ids[1], id))return false;
					board.v[ids[0] / board.c][ids[0] % board.c] = GridType::EMPTY;
					board.v[ids[1] / board.c][ids[1] % board.c] = GridType::EMPTY;
					isUpdate = true;
				}
			}
		}
		return true;
	}
	bool updateLine()
	{
		while (true) {
			bool isUpdate = false;
			if (!updateOpt(isUpdate))return false;
			if (!isUpdate)break;
		}
		return true;
	}
	//如果MAYBE格子旁边的MAYBE+EMPTY(不算isMid的)少于2个,则一定是WALL
	bool opt2()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::MAYBE) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						if (brokenLines.isMid(board.calId(par.first, par.second)))continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n < 2) {
						board.v[i][j] = GridType::WALL;
						ans = true;
					}
				}
			}
		}
		return ans;
	}

	//如果EMPTY格子旁边的MAYBE+EMPTY(不算isMid且不相连的)不超过2个,则旁边的MAYBE一定是EMPTY
	bool opt3()
	{
		bool ans = false;
		for (int i = 0; i < board.r; i++) {
			for (int j = 0; j < board.c; j++) {
				if (board.v[i][j] == GridType::EMPTY) {
					int n = 0;
					vector<pair<int, int>> nb = board.getNeighbor(i, j);
					for (auto par : nb) {
						int id = board.calId(par.first, par.second);
						if (brokenLines.isMid(id) && brokenLines.s.find({ id, board.calId(i, j) }) == brokenLines.s.end())continue;
						if (board.v[par.first][par.second] == GridType::MAYBE || board.v[par.first][par.second] == GridType::EMPTY)n++;
					}
					if (n <= 2) {
						for (auto par : nb) {
							if (board.v[par.first][par.second] == GridType::MAYBE) {
								board.v[par.first][par.second] = GridType::EMPTY;
								ans = true;
							}
						}
					}
				}
			}
		}
		return ans;
	}
	bool check()
	{
		if (!updateLine()) {
			return false;
		}
		while (opt2() || opt3() || opt4(board) || opt5(board)) {
			if (!updateLine()) {
				return false;
			}
		}
		return check4(board) && check5(board);
	}
};

bool dfs(HamiltonCheck& hc, int num, int id)
{
	int r = hc.board.r, c = hc.board.c;
	if (id >= r * c) {
		if (hc.brokenLines.m.size() == 0) {
			hc.board.showWithLine(hc.brokenLines.s);
			return true;
		}
		return false;
	}
	int i = id / c, j = id % c;
	if (hc.brokenLines.isMid(id) || hc.board.v[i][j] == GridType::WALL)return dfs(hc, num, id + 1);
	vector<pair<int, int>> nb;
	for (int k = 1; k <= 2; k++) {
		int i2 = i + dr[k], j2 = j + dc[k];
		if (hc.board.inBoard(i2, j2) && hc.board.v[i2][j2] == GridType::EMPTY && !hc.brokenLines.isMid(hc.board.calId(i2, j2))) {
			if (hc.brokenLines.s.find({ id, hc.board.calId(i2, j2) }) == hc.brokenLines.s.end()) {
				nb.push_back({ i2,j2 });
			}
		}
	}
	if (hc.brokenLines.isEnd(id)) {
		//再选1个
		for (auto par : nb) {
			HamiltonCheck hc2 = hc;
			if (hc2.brokenLines.update(id, hc.board.calId(par.first, par.second)) && dfs(hc2, num, id + 1))return true;
		}
	}
	else {
		//选2个
		if (nb.size() != 2)return false;
		for (auto par : nb) {
			if (!hc.brokenLines.update(id, hc.board.calId(par.first, par.second)))return false;
		}
		if (dfs(hc, num, id + 1))return true;
	}
	return false;
}
bool dfs(HamiltonCheck hc) //只有EMPTY和WALL,判断是否存在经过所有EMPTY的哈密顿圈
{
	int num = 0;
	for (int i = 0; i < hc.board.r; i++) {
		for (int j = 0; j < hc.board.c; j++) {
			if (hc.board.v[i][j] == GridType::EMPTY) {
				num++;
			}
		}
	}
	numInCircle = num;
	return dfs(hc, num, 0);
}

bool checkBoard(Board& board, set<pair<int, int>>& wallPosAns)
{
	if (wallPosAns.empty())return true;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::WALL && wallPosAns.find({ i,j }) == wallPosAns.end())return false;
		}
	}
	for (auto par : wallPosAns) {
		if (board.v[par.first][par.second] == GridType::EMPTY)return false;
	}
	return true;
}
vector<Board> pickVb(vector<Board>& v, set<pair<int, int>>& wallPosAns)
{
	vector<Board>ans;
	for (auto& board : v)if (checkBoard(board, wallPosAns))ans.push_back(board);
	return ans;
}

bool check(Board board, vector<pair<int, int>>& pickWall)
{
	for (auto par : pickWall)board.v[par.first][par.second] = GridType::WALL;
	return HamiltonCheck(board).check();
}
//pickLoc[i][j]是第i组的第j种选取方案中选了哪些格子成为WALL
//difs[i][j]是第i组的第j种选取方案的奇偶差分
//targetDif是目标差分
bool dfs(Board& board, vector<vector<vector<pair<int, int>>>>& pickLoc2, vector<vector<int>>& ids2, set<int>& idOfIds2)
{
	for (auto id : idOfIds2) {
		auto& vid = ids2[id];
		Board board2 = board;
		for (int i = 0; i < vid.size(); i++) {
			for (auto par : pickLoc2[i][vid[i]]) {
				board2.v[par.first][par.second] = GridType::WALL;
			}
		}
		if (!HamiltonCheck(board2).check())continue;
		for (int i = 0; i < board2.r; i++) {
			for (int j = 0; j < board2.c; j++) {
				if (board2.v[i][j] == GridType::MAYBE)board2.v[i][j] = GridType::EMPTY;
				if (board2.v[i][j] == GridType::NUM)board2.v[i][j] = GridType::WALL;
			}
		}
		if (dfs(board2)) {
			cout << "success!!!!!!!!!" << endl;
			return true;
		}
		else {
			cout << "fail" << endl;
		}
	}
	return false;
}
bool dfs(Board& board, vector<vector<vector<pair<int, int>>>>& pickLoc, vector<vector<int>>& difs, vector<vector<vector<pair<int, int>>>>& pickLoc2, vector<vector<int>>& difs2, int targetDif)
{
	vector<vector<int>> ids = Fforeach(pickLoc);
	vector<vector<int>> ids2 = Fforeach(pickLoc2);
	map<int, set<int>>reflection;
	for (int i = 0; i < ids2.size(); i++) {
		int dif = 0;
		for (int j = 0; j < ids2[i].size(); j++) {
			dif += difs2[j][ids2[i][j]];
		}
		Board board2 = board;
		for (int j = 0; j < ids2[i].size(); j++) {
			for (auto par : pickLoc2[j][ids2[i][j]]) {
				board2.v[par.first][par.second] = GridType::WALL;
			}
		}
		if (!HamiltonCheck(board2).check())continue;
		reflection[dif].insert(i);
	}
	vector<pair<Board, int>>vb;
	for (auto vid : ids) {
		int dif = 0;
		for (int i = 0; i < vid.size(); i++) {
			dif += difs[i][vid[i]];
		}
		Board board2 = board;
		for (int i = 0; i < vid.size(); i++) {
			for (auto par : pickLoc[i][vid[i]]) {
				board2.v[par.first][par.second] = GridType::WALL;
			}
		}
		if (!HamiltonCheck(board2).check())continue;
		vb.push_back({ board2,targetDif - dif });
	}
	for (auto par : vb) {
		if (dfs(par.first, pickLoc2, ids2, reflection[par.second]))return true;
	}
	return false;
}
bool killD1AndDFS(Board& board)
{
	//step1:正交分组、统计哪个方向的待枚举箭头最多
	vector<vector<pair<int, int>>> groups;//groups[i]是第i组有哪些MAYBE格子
	vector<int>groupSums;//groupSums[i]是第i组需要选出几个格子成为WALL
	set<int> locByEachDir[4];
	for (auto p : num) {
		for (auto par : p.second) {
			int r = p.first;
			int c = par.first;
			int adir = dir[r][c];
			int alen = len[r][c];
			int anum = 0;
			for (int i = 0; i < alen; i++) {
				r += dr[adir], c += dc[adir];
				if (board.v[r][c] == GridType::WALL)anum++;
			}
			r = p.first, c = par.first;
			if (anum < num[r][c]) {
				groupSums.push_back(num[r][c] - anum);
				vector<pair<int, int>>v;
				for (int i = 0; i < alen; i++) {
					r += dr[adir], c += dc[adir];
					if (board.v[r][c] == GridType::MAYBE)v.push_back({ r,c });
				}
				locByEachDir[adir].insert(groups.size());
				groups.push_back(v);
			}
		}
	}
	int maxNumDir = 0;
	for (int i = 1; i < 4; i++) {
		if (locByEachDir[maxNumDir].size() < locByEachDir[i].size())maxNumDir = i;
	}
	//step2:计算每组的所有方案和对应的奇偶差分
	vector<vector<vector<pair<int, int>>>> pickLoc1, pickLoc2;//pickLoc[i][j]是第i组的第j种选取方案中选了哪些格子成为WALL
	vector<vector<int>>difs1, difs2;//difs[i][j]是第i组的第j种选取方案的奇偶差分
	for (int i = 0; i < groups.size(); i++) {
		vector<vector<pair<int, int>>> ans = GetAllCombina(groups[i], groupSums[i], true);
		vector<vector<pair<int, int>>> ans2;
		vector<int> dif;
		for (auto ansi : ans) {
			if (!check(board, ansi))continue;
			int difi = 0;
			for (auto loc : ansi) {
				if ((loc.first + loc.second) % 2)difi++;
				else difi--;
			}
			dif.push_back(difi);
			ans2.push_back(ansi);
		}
		if (locByEachDir[maxNumDir].find(i) == locByEachDir[maxNumDir].end()) {
			pickLoc2.push_back(ans2);
			difs2.push_back(dif);
		}
		else {
			pickLoc1.push_back(ans2);
			difs1.push_back(dif);
		}
	}
	//step3:计算目标差分
	int targetDif = 0;
	for (int i = 0; i < board.r; i++) {
		for (int j = 0; j < board.c; j++) {
			if (board.v[i][j] == GridType::EMPTY || board.v[i][j] == GridType::MAYBE) {
				if ((i + j) % 2)targetDif++;
				else targetDif--;
			}
		}
	}
	//step4:枚举所有满足目标差分的组合方案
	return dfs(board, pickLoc1, difs1, pickLoc2, difs2, targetDif);
}

int main()
{
	set<pair<int, int>> wallPosAns;

	///////填入可变部分


	numInCircle--;
	map<int, map<int, int>>d = opt1(board);
	while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
	board.show();
	vector<Board> vb4 = killDx(board, d, 4);
	vb4 = pickVb(vb4, wallPosAns);
	cout << endl << "vb4 size=" << vb4.size() << endl;
	vector<Board>vb3;
	for (auto& board : vb4) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		auto vb = killDx(board, d, 3);
		for (auto board : vb) {
			vb3.push_back(board);
		}
	}
	vb3 = pickVb(vb3, wallPosAns);
	cout << "vb3 size=" << vb3.size() << endl;
	for (auto& board : vb3) {
		while (opt2(board) || opt3(board) || opt4(board) || opt5(board));
		if (!check(board))continue;
		auto vb2 = killD2(board, d);
		cout << "这个vb2 size=" << vb2.size() << endl;
		int x2 = 0;
		for (auto board : vb2) {
			if (!checkBoard(board, wallPosAns)) {
				continue;
			}
			cout << "这个vb2 第" << ++x2 << "个" << endl;
			if (!HamiltonCheck(board).check())continue;
			if (killD1AndDFS(board))return 0;
		}
	}
	return 0;
}

十三,数字识别

1,方法一:手搓

简单的区分一下0到4,其中2和4有时候会认错。


//x y w h根据手机屏幕适配
int x = 23; // 裁剪区域的左上角x坐标
int y = 742; // 裁剪区域的左上角y坐标
int siz = 97; //每个格子的尺寸

void getNum()
{
	int r = 12;
	cv::Mat img = cv::imread("D:/1.png", 0);
	cv::Rect cropRegion(x, y, siz * r, siz * r);
	img = img(cropRegion);
	vector<vector<int>>v;
	for (int i = 0; i < r; i++) {
		for (int j = 0; j < r; j++) {
			auto img2 = img(cv::Rect(j * siz + siz / 3, i * siz + siz / 3, siz / 3, siz / 3));
			//cv::imshow("img2", img2);
			//cv::waitKey(0);
			cv::Scalar mean, stddev;
			cv::meanStdDev(img2, mean, stddev); // 注意,mean是一个包含所有通道均值的cv::Scalar对象,对于灰度图,我们只关心第一个值(mean[0])
			double meanBrightness = mean[0]; // 灰度图的亮度均值
			int b = int(meanBrightness);
			//cout << b << " ";
			if (b < 254) {
				int num = 0;
				if (b > 220)num = 1;
				else if (b > 205)num = 2;
				else if (b > 203)num = 4;
				else if (b > 199)num = 3;
				else num = 0;
				v.push_back({ i,j,num });
				cout << "{" << i << "," << j << "," << num << ",     }," << endl;
			}

		}
	}
}

优点:数字格和空格之间绝不会混淆、数字格的坐标绝不会算错
缺点:数字2和4有时候会认错,超过4的不支持识别

2,方法二:用AI自动识别

我的提问词:

这里面的144个格子,有哪些有数字和箭头,数字分别是多少,如果第i行第j列有数字x,方向是y,那就表示成{i,j,x,y},其中行列的范围是0到11,y是0表示向上,1表示向右,2表示向下,3表示向左,表示成纯列表,方便我复制到代码里面

3,测试用例8、9、10

case8:

AI 输出:

[
  {0, 0, 4, 2},
  {0, 7, 1, 3},
  {1, 1, 2, 1},
  {1, 9, 2, 3},
  {2, 7, 1, 1},
  {3, 7, 1, 3},
  {4, 10, 1, 2},
  {5, 3, 2, 3},
  {5, 8, 3, 3},
  {6, 0, 1, 1},
  {7, 4, 2, 1},
  {8, 6, 1, 2},
  {9, 7, 4, 3},
  {9, 8, 4, 3},
  {9, 11, 3, 0},
  {10, 7, 1, 3},
  {11, 11, 4, 0}
]
结果完全正确

算法输出:

case9:

AI输出:

[ {0, 6, 3, 2}, {0, 11, 2, 3}, {1, 9, 2, 3}, {2, 3, 3, 1}, {3, 3, 1, 1}, {6, 5, 1, 3}, {7, 0, 1, 2}, {7, 6, 1, 1}, {7, 11, 2, 0}, {8, 0, 1, 1}, {8, 9, 1, 2}, {9, 7, 1, 1}, {9, 10, 0, 2}, {10, 5, 1, 2}, {10, 11, 1, 0}, {11, 9, 5, 3}, {11, 11, 6, 3} ]

错了4个坐标,正确的应该是:

	vector<NumGrid>n{
		{0, 6, 3, 2},
		{0, 11, 2, 3},
		{1, 9, 2, 3},
		{2, 3, 3, 1},
		{3, 3, 1, 1},
		{6, 5, 1, 3},
		{7, 0, 1, 2},
		{7, 5, 1, 1},
		{7, 10, 2, 0},
		{8, 0, 1, 1},
		{8, 8, 1, 2},
		{9, 7, 1, 1},
		{9, 9, 0, 2},
		{10, 5, 1, 2},
		{10, 11, 1, 0},
		{11, 9, 5, 3},
		{11, 11, 6, 3}
	};
	Board board(12, 12, n);

算法输出:

case10:

AI输出:

[
  {0, 1, 2, 1},
  {0, 11, 3, 3},
  {1, 3, 3, 1},
  {2, 11, 1, 3},
  {3, 0, 2, 1},
  {4, 0, 1, 1},
  {4, 3, 3, 2},
  {4, 4, 2, 0},
  {5, 5, 1, 1},
  {6, 6, 2, 0},
  {7, 1, 2, 1},
  {8, 9, 2, 3},
  {8, 11, 2, 0},
  {9, 0, 2, 0},
  {10, 0, 2, 0},
  {10, 6, 1, 0},
  {10, 9, 2, 0},
  {11, 0, 2, 1}
]
只有1个坐标有问题,把10, 6改成10, 5就好了

算法输出:

十四,最终版本

1,测试用例

case1到case10同上,除了case7都能得到答案。

在2次性能优化之后,case7也能把所有情况枚举完了,但是还是输出无解,显然还存在BUG。

于是我基于case7添加答案得到case11:

	vector<NumGrid>n{
		{0,0,3,  1   },
		{0,1,1,  2   },
		{0,2,3,  1   },
		{0,7,2,  2   },
		{1,6,1,  3   },
		{1,9,2,  2   },
		{2,9,1,  3   },
		{2,10,2,  2   },
		{3,9,3,   3  },
		{4,3,2,   2  },
		{4,5,2,   0  },
		{4,11,2,  2   },
		{5,5,1,    2 },
		{7,6,2,   2  },
		{7,10,2,  2   },
		{11,11,4,  3   },
	};
	Board board(12, 12, n);
	wallPosAns = {
		{0,5},{0,6},{0,11},{1,0},{2,0},{3,0},{3,1},{3,5},{5,7},{5,11},{6,9},{7,9},{7,3},{8,6},{9,6},{10,5},{10,10},{10,11},{11,2},{11,3},{11,7},{11,10}
	};

2,定位结果

首先问题定界,发现killD2Last会把正确答案过滤掉。

再往前一步,输入killD2Last的board和r、c有问题,因为board.v[r][c]已经是WALL了,压根不是MAYBE

3,修复方案

在调用killD2Last之前新增一行即可。

board.v[r][c] = GridType::MAYBE;

随着人类对生命健康需求的不断增长,新药研发面临着前所未有的挑战。传统的药物研发流程通常耗时长达十年以上,耗资数十亿美元,且最终成功率极低,这在制药界被称为“反摩尔定律”困境。近年来,人工智能技术的飞速发展,特别是深度学习和大数据分析的广泛应用,为新药发现带来了革命性的契机。人工智能能够从海量的化学和生物数据中挖掘潜在规律,显著加速药物靶点发现、先导化合物优化等关键环节。在此背景下,本研究旨在设计并实现一个基于人工智能的新药发现辅助系统,以期为传统药物研发流程提供高效的智能化辅助工具,从而有效缩短研发周期并大幅降低研发成本。本研究以Python作为主要开发语言,深度结合PyTorch和TensorFlow两大主流深度学习框架,并集成RDKit化学信息学工具包,构建了一个功能完善的新药发现辅助系统。系统的核心目标是利用先进的人工智能技术辅助新药分子的设计与活性评估。在研究方法上,本文创新性地提出了一种融合多模态数据的新药发现算法。该算法综合处理分子的多种表示形式,包括一维的SMILES序列、二维的分子图结构以及三维的空间构象数据。通过构建多通道神经网络,系统能够有效提取并融合不同模态的特征,从而全面捕捉分子的理化性质与生物学活性之间的复杂非线性关系。 【课程报告内容】 摘要 第1章 绪论 第2章 相关技术与理论 第3章 系统需求分析 第4章 系统总体设计 第5章 系统详细设计与实现 第6章 系统测试与分析 第7章 总结与展望 参考文献 附件-实现指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值