目录
〇,暗码回路
参考最强大脑第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;
暗码回路&spm=1001.2101.3001.5002&articleId=158979711&d=1&t=3&u=16909c56641742c0b8708b5898708edb)
1万+

被折叠的 条评论
为什么被折叠?



