心路历程

根据题面的描述,我们面临的问题无非是,每次将色块更新成什么颜色。又因为是从左上角开始更新,所以我的有了第一个想法。

将左上角的色块命名为“原色块”。

对于每个色块,定义4中状态:

0-
不属于原色块势力,和原色块势力不邻接,未没有进行任何操作;

1-
不属于原色块势力,和原色块势力邻接,尚不知道会不会被同化(因为还不知道下一步染什么色);

2-
属于原色块势力,和非原色块势力邻接,是上一批被同化的色块;

3-
属于原色块势力,和非原色块势力不邻接**;

我们可以形象地依次把这四种状态称作“未加入”,“待审核”,“新成员”,“老成员”

以下图为例(颜色是色块,数字是状态):

第一步比较特殊,因为需要求出最初的新队员。(之后就只需要处理新成员和待审核就可以了,因为新成员处于邻接位置,需要承担审核工作。而老成员和未加入不需要做任何工作)

首先原色块自身是新成员,然后跑一遍bfs,求出所有与原色块连通且与原色块同色的色块,将它们都标记为新成员。

之后的每一步,首先统计与新成员邻接且未加入的色块,将它们标记为待审核,并统计每种颜色出现的次数。

出现次数最多的颜色就是我们即将更新的颜色。得到这个之后新成员的工作就做完了,新成员就可以标记为老成员了。

再次遍历所有待审核的色块,若颜色和即将更新的颜色相同,则标记为新成员;否则重新标记为未加入。

将所有新成员、老成员的颜色更新。

每次都需要判断是否更新完整个图。

这个算法逻辑上是行得通的,但是太慢了,因为要反复地遍历各种状态,导致大量无用的重复运算。

正解

标算:IDA*

先不谈IDA*。我们回到这道题,从模块化编程的角度出发,想想这个程序要实现什么功能。

首先我们有更新色块的操作。对于更新色块的函数,我们需要3个形参:当前色块的横坐标、纵坐标、我们即将更新的颜色。我们仍然需要一个打标记的数组:若当前位置的颜色和即将更新的颜色相同则标记为1,反之则标记为2.所以这个函数的主要功能是给需要更新颜色的色块打标记,还没有进行上色操作。

void update_flags(int i,int j,int color)//color是即将更新的颜色
{
if(board[i][j]!=color)
{
flags[i][j]=2;//和即将更新的颜色不同,打上标记2
return ;
}
flags[i][j]=1;//和即将更新的颜色相同,打上标记1
for(int k=0;k<4;k++)
{
//dir[][]用于枚举4个方向
int x=i+dir[k][0];
int y=j+dir[k][1];
if(x<0||y<0||x>=siz||y>=siz||flags[x][y]) continue;
update_flags(x,y,color);//递归更新尚未打标记的色块
}
}

然后考虑具体的上色操作。最外层循环遍历6种颜色,然后遍历全图。若当前遍历到的位置是不同颜色且正好等于要搜索的颜色,则记录存在这种颜色,并进行\(update_flags\).若全图遍历完后发现不存在这种颜色,则继续遍历下一个颜色。如果更新了棋盘,就递归搜索,成功了就返回。注意:\(flags\)数组在这个过程中可能会被改变,为了后续的使用,我们需要在操作前拷贝\(flags\)到\(tmp\)数组,操作结束后再拷贝回去。

接下来再考虑使用IDA.IDA可以理解为以迭代加深DFS的搜索框架为基础,把原来简单的深度限制加强为:若当前深度+未来估计步数>深度限制,则立即从当前分支回溯

具体到本题,我们的估价函数就应该是:如果颜色数量大于剩余步数,则行不通。所以我们还需要一个计算剩余颜色数量的函数。

int color_cnt()
{
int ret=0;
int s=0;
for(int i=0;i<siz;i++)
for(int j=0;j<siz;j++)
if(flags[i][j]!=1)
s|=(1<<board[i][j]);
while(s)//检查还有几种颜色
{
ret+=s&1;
s>>=1;
}
return ret;
}

并且,我们要在主函数中枚举每一种最大限定深度。

最终代码呈现如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=15;
int siz;
int board[N][N];
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int flags[N][N];
int max_depth;
void update_flags(int i,int j,int color)//color是即将更新的颜色
{
if(board[i][j]!=color)
{
flags[i][j]=2;//和即将更新的颜色不同,打上标记2
return ;
}
flags[i][j]=1;//和即将更新的颜色相同,打上标记1
for(int k=0;k<4;k++)
{
//dir[][]用于枚举4个方向
int x=i+dir[k][0];
int y=j+dir[k][1];
if(x<0||y<0||x>=siz||y>=siz||flags[x][y]) continue;
update_flags(x,y,color);//递归更新尚未打标记的色块
}
}
int color_cnt()
{
int ret=0;
int s=0;
for(int i=0;i<siz;i++)
for(int j=0;j<siz;j++)
if(flags[i][j]!=1)
s|=(1<<board[i][j]);
while(s)//检查还有几种颜色
{
ret+=s&1;
s>>=1;
}
return ret;
}
int dfs(int depth)
{
//如果颜色数量大于剩余步数,则行不通(估价函数)
if(color_cnt()>max_depth-depth)
return 0;
//如果当前深度刚好等于最大深度,说明已经找完了
if(depth==max_depth)
return 1;
int tmp[N][N];
int color_exist;
memcpy(tmp,flags,sizeof(flags));
for(int color=0;color<6;color++)
{
color_exist=0;
for(int i=0;i<siz;i++)
for(int j=0;j<siz;j++)//当前位置是不同颜色且正好等于要搜索的颜色
if(flags[i][j]==2&&board[i][j]==color)
{
color_exist=1;
update_flags(i,j,color);
}
//颜色不存在,继续搜索
if(!color_exist) continue;
//如果更新了棋盘,就递归搜索,成功则返回
if(dfs(depth+1)) return 1;
memcpy(flags,tmp,sizeof(flags));
}
return 0;
}
int main()
{
while(scanf("%d",&siz)&&siz)
{
for(int i=0;i<siz;i++)
for(int j=0;j<siz;j++)
scanf("%d",&board[i][j]);
//限定最大深度
for(max_depth=0;;max_depth++)
{
memset(flags,0,sizeof(flags));
update_flags(0,0,board[0][0]);
if(dfs(0)) break;
}
printf("%d\n",max_depth);
}
return 0;
}

参考

flood_it 方法记录的更多相关文章

  1. EF里查看/修改实体的当前值、原始值和数据库值以及重写SaveChanges方法记录实体状态

    本文目录 查看实体当前.原始和数据库值:DbEntityEntry 查看实体的某个属性值:GetValue<TValue>方法 拷贝DbPropertyValues到实体:ToObject ...

  2. 64位 SQL Server2008链接访问Oracle 过程汇总解决方法记录

    64位 SQL Server2008链接访问Oracle 过程汇总解决方法记录 经过几天不停的网上找资料,实验,终于联通了. 环境:系统:win 2008 ,SqlServer2008 R2, 连接O ...

  3. js实用方法记录-js动态加载css、js脚本文件

    js实用方法记录-动态加载css/js 附送一个加载iframe,h5打开app代码 1. 动态加载js文件到head标签并执行回调 方法调用:dynamicLoadJs('http://www.yi ...

  4. js实用方法记录-简单cookie操作

    js实用方法记录-简单cookie操作 设置cookie:setCookie(名称,值,保存时间,保存域); 获取cookie:setCookie(名称); 移除cookie:setCookie(名称 ...

  5. js实用方法记录-指不定哪天就会用到的js方法

    js实用方法记录-指不定哪天就会用到的js方法 常用或者不常用都有 判断是否在微信浏览器中 测试代码:isWeiXin()==false /** * 是否在微信中 */ function isWeix ...

  6. Java给各个方法记录执行时间

    Java给各个方法记录执行时间 long startTime = System.currentTimeMillis();...//要测试时间的方法LoggerFactory.getLogger(Bas ...

  7. make menuconfig error 解决方法记录

    新建的一个虚拟机,发现make menuconfig 后会出错,查了一下是缺少一些库. 这个错误已经错了两次了,我不希望第三次出现了还想不起来,所以特此记录. # 错误信息: make[2]: *** ...

  8. 简单一键CENTOS6 安装PPTP VPN方法记录

    申明:我们使用PPTP VPN仅仅只能用在查阅资料等正规渠道,不要用在不良用途上.方法收集于网上,这里我用在搬瓦工VPS(VPS方案直达),采用的是CENTOS6 64位系统.我们需要预先将VPS服务 ...

  9. ASP.NET页面优化性能提升方法记录

    今天与大家分享:一种优化页面执行速度的方法.采用这个方法,可以使用页面的执行速度获得[8倍]的提升效果. 为了让您对优化的效果有个直观的了解,我准备了下面的测试结果截图: 测试环境:1. Window ...

随机推荐

  1. 钡铼BL102分布式IO系统如何应用于锂电池行业

    近年来,全球新能源汽车的蓬勃发展促进了锂电池行业的发展.随着锂电池标准化程度的提高,电池和模块规格的标准化是未来的发展趋势,也促进了自动化模块生产线的发展. 锂电池模块生产线通过涂胶-电池堆叠-组装- ...

  2. 利用本地HTTPS模拟环境为FastAPI框架集成FaceBook社交三方登录

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_174 提起社交,就不得不说马克·扎克伯格(Mark Zuckerberg)一手创办的社交网络(FaceBook).进入2020年, ...

  3. DolphinScheduler 源码剖析之 Master 容错处理流程

    点击上方蓝字关注 Apache DolphinScheduler Apache DolphinScheduler(incubating),简称"DS", 中文名 "海豚调 ...

  4. 【docker专栏8】使用IDEA远程管理docker镜像及容器服务

    使用命令行的方式管理服务器镜像及容器是运维人员最常用的方式,但是有的时候我们不得不远程操作docker或者是面向对docker并不熟悉的技术人员提供能力(配置管理员.测试人员),这种情况下图形界面就有 ...

  5. 搞定面试官 - MySQL 中你知道如何计算一个索引的长度嘛?

    大家好,我是程序员啊粥. 今天给大家分享一个我遇到过的比较少见的面试题,那就是 MySQL 中如何计算一个索引的长度. 说实话,我第一次遇到这个问题的时候想当然的以为索引长度就是我们建表时定义的字段长 ...

  6. Windows 查看端口占用并关闭

    在启动服务的时候,可能会遇到端口被占用的情况. 这时候就需要知道哪个服务占用了这个端口,并将其关闭. 然后再启动服务就不会存在端口占用了. 这里以 Tomcat 的默认端口 8080 为例. 打开命令 ...

  7. kali下安装Nessus

    Kali安装Nessus 说明 Nessus是一款基于插件的系统漏洞扫描和分析软件 一.安装 1.下载安装包 https://www.tenable.com/downloads/nessus 查看ka ...

  8. 树莓派4B无屏幕连接Wi-Fi/启用ssh/创建用户

    前边总得说点什么 最近每次在Win10上写代码需要启动Redis,残血Redis For Windows有卡死系统的bug.由于主机内存不大够用(已经扩到顶了),开虚拟机运行Redis更别提了..想起 ...

  9. Vuex与前端表格施展“组合拳”,实现大屏展示应用的交互增强

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 下图是一个产品开发中非常常见的大屏展示界面 ...

  10. 你言我语 By Twikoo

    主要做了两件事: 一是前端魔改 二是首页调用(替代原 bber) 注明:以下样式.功能代码基于 Twikoo v1.6.4 前端魔改 "管理面板"按钮同步隐藏输入框.先到twiko ...