【2024蓝桥杯/C++/B组/数字接龙】

题目

问题代码

#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;

   }

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值