题目
问题代码
#include<bits/stdc++.++.h>
using namespace std;
// 定义对<int, int>的别名,方便后续代码的书写
#define PII pair<int, int>
// 对<int, int>的第一个元素进行访问
#define x first
// 对<int, int>的第二个元素进行访问
#define y second
// 用于标记已经走过的格子
int sign[20][20];
// 用于标记已经走过的斜线范围
//例如cross[1][0][1][0]代表1行0行1列0列确定的田字格中已存在对角线,不能再来对角线了
int cross[20][20][20][20];
// 棋盘状态
int chess[20][20];
// 8个可能的方向
PII direc[8] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
// 存储结果
string result;
// 用于计算斜线范围
int r1, r2, c1, c2;
// n代表棋盘大小,k代表k进制
int n, k;
// 深度优先搜索函数
void dfs(int fx, int fy, int tx, int ty, string tmp, int cnt)
{
// 如果已经找到解,则直接返回
if(!result.empty()) return;
// 到达终点
if(fx == tx && fy == ty)
{
// 如果已经遍历了所有格子
if(cnt == n*n) result = tmp;
return;
}
int nx, ny;
// 遍历八个方向
for(int i = 0; i < 8; i++)
{
// 计算下一步的位置
nx = fx + direc[i].x;
ny = fy + direc[i].y;
// 如果超出边界则跳过
if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
// 如果已经访问过则跳过
if(sign[nx][ny]) continue;
// 如果不符合数字顺序则跳过
if(chess[nx][ny] != (chess[fx][fy]+1) % k) continue;
// 如果是斜向移动
if(i % 2)
{
// 计算影响的的行列范围
r1 = max(fx, nx);
r2 = min(fx, nx);
c1 = max(fy, ny);
c2 = min(fy, ny);
// 如果已经走过这范围(下步会交叉)则跳过
if(cross[r1][r2][c1][c2]) continue;
else cross[r1][r2][c1][c2] = 1;
}
// 标记当前格子已访问
sign[nx][ny] = 1;
// 递归调用
dfs(nx, ny, tx, ty, tmp + (char)(i+'0'), cnt+1);
// 回溯
sign[nx][ny] = 0;
if(i % 2) cross[r1][r2][c1][c2] = 0;
}
}
// 主函数
int main()
{
// 输入n和k
cin >> n >> k;
// 读取棋盘的状态
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
cin >> chess[i][j];
}
}
// 初始化字符串
string tmp;
// 标记起点
sign[0][0] = 1;
// 调用dfs函数
dfs(0, 0, n-1, n-1, tmp, 1);
// 输出结果
if(result.empty()) cout << -1;
else cout << result;
}
问题1(已经解决)
全局声明了变量,但是主函数内又声明了,导致主函数内的赋值结果函数使用不到。
问题2(已经解决)
跑出来结果是
问题在于没有严格回溯完全。r1\r2\c1\c2也需要回溯。本质上是这个设置为全局变量造成的。
修改对角线处理后的正确答案(处理思路不同,原来的思路没发现问题)
此时cross[x1][y1][x2][y2]表示一条有向线段的存在标记
当前有向线段为从fxfy到nxny,其中的对应线段要么是fxny到nxfy,要么是nxfy,fxny
如果不考虑双向就考虑一个方向,但是判断就得两个方向都判断。因为设置不全面,所以要么都没有要么只有一个。
如果考虑双向就全部设置,判断就可以轻松一点,因为要么都没有要么都有,随便哪个都行。(本代码采用后者)
#include<bits/stdc++.h>
using namespace std;
// 定义二维对的类型
#define PII pair<int, int>
// 第一个元素为x坐标
#define x first
// 第二个元素为y坐标
#define y second
// 签名数组,记录节点是否访问过
int sign[20][20];
// 交叉数组,记录两个节点间是否已经建立过连接
int cross[20][20][20][20];
// 棋盘数组,记录每个格子的颜色
int chess[20][20];
// 八个可能的移动方向
PII direc[8] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
// 存储结果路径的字符串
string result;
// 辅助变量,用于记录起始点和终止点的坐标
int r1, r2, c1, c2;
// n表示棋盘大小,k表示棋盘中不同颜色的数量
int n, k;
// DFS函数实现
void dfs(int fx, int fy, int tx, int ty, string tmp, int cnt)
{
// 如果已经找到一条解,直接返回
if(!result.empty()) return;
// 当前位置与目标位置相同
if(fx == tx && fy == ty)
{
// 如果已经遍历了所有格子,则更新结果
if(cnt == n*n) result = tmp;
// 结束递归
return;
}
// 用于存储下一个节点的坐标
int nx, ny;
// 遍历八个可能的移动方向
for(int i = 0; i < 8; i++)
{
// 计算下一个节点的坐标
nx = fx + direc[i].x;
ny = fy + direc[i].y;
// 越界检查
if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
// 已经访问过这个节点
if(sign[nx][ny]) continue;
// 下一个节点的颜色不符合规则
if(chess[nx][ny] != (chess[fx][fy]+1) % k) continue;
// 检查是否已经建立过这条连线
if (i % 2)
{
if (!cross[fx][ny][nx][fy]) {
cross[fx][fy][nx][ny] = true;
cross[nx][ny][fx][fy] = true;
}
else continue;
}
// 标记当前位置为已访问
sign[nx][ny] = 1;
// 递归调用dfs,传入新的位置信息以及路径信息
dfs(nx, ny, tx, ty, tmp + (char)(i + '0'), cnt + 1);
// 回溯,重置标志位
sign[nx][ny] = 0;
if (i % 2) {
cross[fx][fy][nx][ny] = false;
cross[nx][ny][fx][fy] = false;
}
}
}
int main()
{
// 输入棋盘大小和颜色数量
cin >> n >> k;
// 读取棋盘数据
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
cin >> chess[i][j];
}
}
// 初始化临时路径为空
string tmp;
// 标记起点为已访问
sign[0][0] = 1;
// 从起点开始DFS搜索
dfs(0, 0, n-1, n-1, tmp, 1);
// 如果未找到路径,输出-1
if(result.empty()) cout << -1;
// 否则输出路径
else cout << result;
return 0;
}
正确代码(沿用1思路)
#include<bits/stdc++.h>
using namespace std;
// 定义对<int, int>的别名,方便后续代码的书写
#define PII pair<int, int>
// 对<int, int>的第一个元素进行访问
#define x first
// 对<int, int>的第二个元素进行访问
#define y second
// 用于标记已经走过的格子
int sign[20][20];
// 用于标记已经走过的斜线范围
//例如cross[1][0][1][0]代表1行0行1列0列确定的田字格中已存在对角线,不能再来对角线了
int cross[20][20][20][20];
// 棋盘状态
int chess[20][20];
// 8个可能的方向
PII direc[8] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
// 存储结果
string result;
// n代表棋盘大小,k代表k进制
int n, k;
// 深度优先搜索函数
void dfs(int fx, int fy, int tx, int ty, string tmp, int cnt)
{
// 如果已经找到解,则直接返回
if(!result.empty()) return;
// 到达终点
if(fx == tx && fy == ty)
{
// 如果已经遍历了所有格子
if(cnt == n*n) result = tmp;
return;
}
int nx, ny;
// 遍历八个方向
for(int i = 0; i < 8; i++)
{
// 计算下一步的位置
nx = fx + direc[i].x;
ny = fy + direc[i].y;
// 如果超出边界则跳过
if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
// 如果已经访问过则跳过
if(sign[nx][ny]) continue;
// 如果不符合数字顺序则跳过
if(chess[nx][ny] != (chess[fx][fy]+1) % k) continue;
int r1, r2, c1, c2;
// 如果是斜向移动
if(i % 2)
{
// 计算影响的的行列范围
r1 = max(fx, nx);
r2 = min(fx, nx);
c1 = max(fy, ny);
c2 = min(fy, ny);
// 如果已经走过这范围(下步会交叉)则跳过
if(cross[r1][r2][c1][c2]) continue;
else cross[r1][r2][c1][c2] = 1;
}
// 标记当前格子已访问
sign[nx][ny] = 1;
// 递归调用
dfs(nx, ny, tx, ty, tmp + (char)(i+'0'), cnt+1);
// 回溯
sign[nx][ny] = 0;
if(i % 2) cross[r1][r2][c1][c2] = 0;
}
}
// 主函数
int main()
{
// 输入n和k
cin >> n >> k;
// 读取棋盘的状态
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
cin >> chess[i][j];
}
}
// 初始化字符串
string tmp;
// 标记起点
sign[0][0] = 1;
// 调用dfs函数
dfs(0, 0, n-1, n-1, tmp, 1);
// 输出结果
if(result.empty()) cout << -1;
else cout << result;
}