当前位置: 首页 > news >正文

【算法竞赛】dfs+csp综合应用(蓝桥2023A9像素放置)

目录

一、 题目

二、思路

(1)算法框架选择

(2)剪枝策略

具体来说就是:

三、代码

(1) 数据读取与初始化

(2) 检查当前填充是否符合要求

(3) 递归 DFS 进行填充

(4) 读取输入 & 调用 DFS

(5) 完整代码


一、 题目

9.像素放置 - 蓝桥云课

【将题目翻译过来就是】:

该游戏基于 n × m 的网格棋盘进行,每个网格可以是白色或黑色,并且某些网格内标注了数字 x(0 ≤ x ≤ 9)。

核心规则:

  • 每个带数字 x 的网格周围 8 个相邻格子(上下左右 + 左上、左下、右上、右下)中,必须恰好有 x 个格子被填充为黑色。

  • 其余未标注数字的网格可以自由填充。

  • 最终输出符合条件的填充方案,白色格子用 0 表示,黑色格子用 1 表示。

二、思路

这道题本质上是一个约束满足问题(CSP),每个数字格子对其九宫格区域内的黑色格子数施加了严格约束,我们需要在满足所有约束的条件下找到唯一的合法解,个人认为这个问题的挑战在于:

  1. 所有约束相互关联,修改一个格子可能影响多个数字区域

  2. 需要同时处理正向推导(根据已知约束确定颜色)和反向验证(尝试填充后检查约束)

  3. 必须确保解的唯一性

(1)算法框架选择

我们采用DFS框架,但与我们一般用传统DFS解题的区别在于:

  1. 非路径式搜索:不是寻找路径,而是构建完整的二维状态

  2. 顺序填充:按行优先顺序逐个填充格子(0,0)→(0,1)→...→(n-1,m-1)

  3. 随时验证:在填充过程中随时验证已确定的约束

(2)剪枝策略

当处理到格子(x,y)时,其左上方的格子(x-1,y-1)的后续填充操作不会再改变该格子的状态,这其实也就是说:

  1. 在填充(x,y)后,必须立即验证(x-1,y-1)的约束

  2. 当处理到行末(y=m-1)时,还需验证正上方格子(x-1,y)的约束

这种剪枝策略基于"最早失败"原则——一旦发现局部约束不满足,立即停止当前搜索路径,可以比较有效的减少时间复杂度

具体来说就是:
当前处理位置必须验证的约束位置
非行末(y<m-1)(x-1,y-1)
行末(y=m-1)(x-1,y-1)和(x-1,y)

弄明白了这两个问题,其实这个问题就比较轻松了,核心就是如何实现约束 

三、代码

(1) 数据读取与初始化

#include <iostream>
#include <algorithm>
using namespace std;

const int MAX_N = 15;
int n, m;
char map[MAX_N][MAX_N];
int f[MAX_N][MAX_N]; // 存储填色方案
  • 由于 n, m ≤ 15,可以使用 15 × 15 的二维数组存储棋盘数据。

  • map[][] 用于存储输入的棋盘布局。

  • f[][] 用于存储填充方案(0 代表白色,1 代表黑色)。

(2) 检查当前填充是否符合要求

bool check(int x, int y) {
    if (map[x][y] == '_') return true; // 没有限制的网格,直接返回 true
    int num = map[x][y] - '0';
    int cnt = 0; // 统计周围黑色格子的数量
    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            int newX = x + i, newY = y + j;
            if (newX < 0 || newX >= n || newY < 0 || newY >= m) continue; // 越界检查
            if (f[newX][newY] == 1) cnt++; // 统计黑色格子
        }
    }
    return cnt == num; // 若黑色格子数量符合 `x`,则返回 true
}
  • 遍历 3×3 的范围,统计黑色格子数量。

  • 若符合 x 约束,返回 true,否则返回 false

(3) 递归 DFS 进行填充

void dfs(int x, int y) {
    if (x == n) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) cout << f[i][j];
            cout << endl;
        }
        return;
    }
    // 计算下一步的坐标
    int nextX = (y == m - 1) ? x + 1 : x;
    int nextY = (y == m - 1) ? 0 : y + 1;
    
    // 先尝试填黑色
    f[x][y] = 1;
    if ((x == 0 || y == 0 || check(x-1, y-1)) && (y == m - 1 || check(x-1, y)))
        dfs(nextX, nextY);
    
    // 尝试填白色
    f[x][y] = 0;
    if ((x == 0 || y == 0 || check(x-1, y-1)) && (y == m - 1 || check(x-1, y)))
        dfs(nextX, nextY);
}
  • 递归填充每个网格,并在 x == n 时输出方案。

  • 由于每个格子可以填 01,因此分别尝试后递归调用 dfs

  • 在填充后,检查左上角及已填充区域是否仍满足 x 约束。

(4) 读取输入 & 调用 DFS

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            scanf(" %c", &map[i][j]); // 读取地图数据
        }
    }
    dfs(0, 0); // 递归填充
    return 0;
}
  • 读取棋盘信息,并调用 dfs(0,0) 开始填充。

  • 由于 scanf(" %c") 前有空格,可自动跳过空白符(空格、换行)。

(5) 完整代码

//每个格子都要填到,但是可能重复访问,比如先填黑色,再填白色
//如何解决重复访问且保证不会死循环:按顺序遍历格子
#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
char map[15][15];
int f[15][15];//填色方案
bool check(int x,int y)
{
  if(map[x][y]=='_') return true;
  else 
  {
    int num=map[x][y]-'0';
    int cnt=0;//涂黑的个数
    for(int i=-1;i<=1;i++)
    {
      for(int j=-1;j<=1;j++)
      {
        int newx=x+i,newy=y+j;
        if(newx<0||newx>=n||newy<0||newy>=m) continue;
        if(f[newx][newy]==1) cnt++;
      }
    }
    if(cnt==num) return true;
    else return false;
  }
}
void dfs(int x,int y)//剩余的可以填黑色的方格数,已经填过的方格数
{
  //按逐行逐列的顺序填充
  //全部填充完
  if(x==n) 
  {
    //检查最后一行是否全满足条件
    //(还剩最后一行没有检查)
    for(int y=0;y<m;y++)
    {
      if(!check(n-1,y)) return;//该方案不符合条件
    }
    //输出符合条件的方案
    for(int i=0;i<n;i++)
    {
      for(int j=0;j<m;j++) cout<<f[i][j];
      cout<<endl;
    }
    return;
  }
  //每次填充完当前格子检查左上方格子是否合格
  //因为它的状态不会再被影响了,已经固定了
  //到达最后一列的情况要特殊判断,不仅要判断左上方还要判断上方,还涉及到换行
  if(y==m-1)
  {
    //填黑色
    f[x][y]=1;
    //第一行不用检查,第一列只用检查上方
    if(x==0||check(x-1,y-1)&&check(x-1,y)||y==0&&check(x-1,y)) dfs(x+1,0);//换行
    //填白色 
    f[x][y]=0;
    if(x==0||check(x-1,y-1)&&check(x-1,y)||y==0&&check(x-1,y)) dfs(x+1,0);//换行
  }
  //最后一列之前只用检查左上方
  else
  {
    //填黑色
    f[x][y]=1;
    if(x==0||y==0||check(x-1,y-1)) dfs(x,y+1);
    //填白色
    f[x][y]=0;
    if(x==0||y==0||check(x-1,y-1)) dfs(x,y+1);
  }
}
int main()
{
  cin>>n>>m;
  for(int i=0;i<n;i++)
  {
    //scanf前面加空格读换行 
    for(int j=0;j<m;j++) cin>>map[i][j];                   
  }
  dfs(0,0);
  return 0;
}

http://www.dtcms.com/a/111578.html

相关文章:

  • 深入理解时间复杂度与空间复杂度
  • DeepSeek能否用于对话系统(Chatbot)?技术解析与应用实例!
  • 《AI大模型应知应会100篇》第3篇:大模型的能力边界:它能做什么,不能做什么
  • 3.29-3 压力测试(不同用户)
  • 搭建工作流自动化工具n8n并配置deepseek大模型
  • day23学习Pandas库
  • python基础-10-组织文件
  • 6. RabbitMQ 死信队列的详细操作编写
  • linux3 mkdir rmdir rm cp touch ls -d /*/
  • The emulator process for AVD xxx has terminated
  • 部署nerdctl工具
  • A2DP(Advanced Audio Distribution Profile)是蓝牙协议栈中用于音频传输的一个标准化协议
  • Java8 Stream流:过滤、归约与并行计算
  • VirtualBox中安装Win10教程
  • Joomla教程—常用模块 - 登录模块与常用模块 - 文章列表
  • RISC-V debug专栏1 --- Introduction
  • 杰文字悖论:效率提升的副作用
  • 文档处理利器Docling,基于LangChain打造RAG应用
  • 【Cursor/VsCode】在文件列表中不显示.meta文件
  • Vue 项目使用 pdf.js 及 Elasticpdf 教程
  • pyTorch框架:模型的子类写法--改进版二分类问题
  • 【Ragflow】11. 文件解析流程分析/批量解析实现
  • 计算机视觉算法实战——基于YOLOv8的自动驾驶障碍物实时感知系统
  • linux gcc
  • 【读书笔记·VLSI电路设计方法解密】问题62:什么是故障覆盖率,什么是百万缺陷率
  • 【算法/c++】利用中序遍历和后序遍历建二叉树
  • Axure RP 9 详细图文安装流程(附安装包)教程包含下载、安装、汉化、授权
  • 3维格式转换(二)
  • AI医疗诊疗系统设计方案
  • Qt QComboBox 下拉复选多选