CSP认证准备第四天-BFS(双端BFS/0-1BFS)和DFS

article/2025/6/20 7:29:37

B. Chamber of Secrets

参考资料:

问题 - 173B - Codeforces

还是上次那到题目,纯看代码有点看不懂,让ai解释一下:

  • add_front:将状态添加到队列前端(用于不增加代价的移动)

  • add_back:将状态添加到队列后端(用于增加代价的移动)

void add_front(int x, int y, int dir, int d) {if (d < dist[x][y][dir]) {      //只有当我们找到更小的 d 时才需要更新 dist 和加入队列dist[x][y][dir] = d;q.push_front(dir);q.push_front(y);q.push_front(x);}
}void add_back(int x, int y, int dir, int d) {if (d < dist[x][y][dir]) {dist[x][y][dir] = d;q.push_back(x);q.push_back(y);q.push_back(dir);}
}
  1. 双端队列BFS:处理0-1权重图的最短路径问题,0代价的操作放队首,1代价的操作放队尾。

  2. 状态表示:使用三维数组dist[x][y][dir]记录到达位置(x,y)时方向为dir的最小代价。

  3. 反向搜索:从终点开始搜索到起点,与正向搜索等价但实现上更方便。

BFS主循环:

while (!q.empty()) {int x = q[0], y = q[1], dir = q[2];q.pop_front(); q.pop_front(); q.pop_front();int d = dist[x][y][dir];// 尝试沿当前方向移动(不改变方向,不增加代价)int nx = x + fx[dir], ny = y + fy[dir];if (nx >= 0 && nx < n && ny >= 0 && ny < m)add_front(nx, ny, dir, d);// 如果当前格子是障碍物,可以改变方向(增加1代价)if (grid[x][y] == '#')for (int i = 0; i < 4; i++)if (i != dir) add_back(x, y, i, d + 1);
}

      这道题让我疑惑的就是方向,注意这里的坐标原点为左上角,所以如果x不变,y-1,是往左移动的。不要受x、y命名的影响,这里x就是行,y就是列,行号不变,列号减小就是往左移动。完整代码:

#include <deque>
#include <iostream>
using namespace std;constexpr int INF = 1 << 29;  // 定义无穷大值,表示不可达
int n, m;                     // 网格的行数和列数
char grid[1001][1001];        // 存储网格数据,'#'表示障碍物,'.'表示空地// dist[x][y][dir] 表示到达位置(x,y)且方向为dir时的最小代价
int dist[1001][1001][4];// 四个方向的移动增量:下、上、右、左
int fx[] = {1, -1, 0, 0};
int fy[] = {0, 0, 1, -1};deque<int> q;  // 双端队列,用于实现0-1 BFSvoid add_front(int x, int y, int dir, int d) {// 只有找到更小的代价时才更新if (d < dist[x][y][dir]) {dist[x][y][dir] = d;  // 更新最小代价// 将状态压入队列前端(x、y、dir三个值)// 注意顺序:先压dir,最后压x,这样取出时顺序就是x,y,dirq.push_front(dir);q.push_front(y);q.push_front(x);}
}void add_back(int x, int y, int dir, int d) {if (d < dist[x][y][dir]) {dist[x][y][dir] = d;// 将状态压入队列后端(x、y、dir三个值)q.push_back(x);q.push_back(y);q.push_back(dir);}
}int main() {// 读取输入cin >> n >> m;for (int i = 0; i < n; i++) {cin >> grid[i];}// 初始化距离数组为无穷大for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {for (int k = 0; k < 4; k++) {dist[i][j][k] = INF;}}}// 从终点(n-1,m-1)开始搜索,初始方向为左(3),代价为0add_front(n - 1, m - 1, 3, 0);// 开始双端队列BFSwhile (!q.empty()) {// 取出队首状态int x = q[0];int y = q[1];int dir = q[2];q.pop_front();q.pop_front();q.pop_front();int d = dist[x][y][dir];  // 当前状态的代价// 尝试沿当前方向移动(不改变方向,不增加代价)int nx = x + fx[dir];int ny = y + fy[dir];// 检查新位置是否在网格内if (nx >= 0 && nx < n && ny >= 0 && ny < m) {// 将新状态加入队首(代价不变)add_front(nx, ny, dir, d);}// 如果当前格子是障碍物,可以改变方向(需要花费1代价)if (grid[x][y] == '#') {// 尝试所有其他三个方向for (int i = 0; i < 4; i++) {if (i != dir) {// 将新状态加入队尾(代价+1)add_back(x, y, i, d + 1);}}}}// 输出结果:起点(0,0)方向为左(3)的最小代价if (dist[0][0][3] == INF) {cout << -1 << endl;  // 不可达} else {cout << dist[0][0][3] << endl;  // 输出最小代价}return 0;
}

第36次CCF认证-第四题

相关文件: TUOJ

参考题解:

CCF-CSP第36次认证第四题——跳房子【NA!巧妙利用BFS】_csp跳房子-CSDN博客

第36次ccf-csp题解(思维) - devoteeing - 博客园

感觉学完BFS之后,这道题并不难。和模版差不了多少,就是隔了一个n,如果遍历的时候,下一个位置为n,则可以提前结束循环。

#include <bits/stdc++.h>
using namespace std;
int main()
{int n;scanf("%d", &n);vector<int> a(n + 1);vector<int> k(n + 1);vector<int> step(n + 1, -1);step[1] = 0;queue<int> q;q.push(1); //先把第一个位置push进去for (int i = 1; i <= n; i++){scanf("%d", &a[i]);}for (int i = 1; i <= n; i++){scanf("%d", &k[i]);}while (!q.empty()){int x = q.front();q.pop();if (x == n) break; //已经到达终点n,可以提前跳出,可以避免超时?int l = x + 1;  //跳跃区间最左侧int r = min(n, x + k[x]);   //跳跃区间最右侧if (r == n) {step[n] = step[x] + 1;break;}for (int i = r; i >= l; i--)   //从右往左遍历{int next = i - a[i];             //注意还需要往回退if (step[next] == -1)  //说明还没有遍历到{step[next] = step[x] + 1;q.push(next);       //注意是在这里push的,而不是在for循环里push}                            //注意BFS的特点,一旦遍历到了某个节点,这个节点的距离就是最小的,定死了。}}cout << step[n];return 0;
}

这个版本的代码超时了,只拿到了30分

另一篇满分题解:

  代码中使用了BFS,但是与之前不同,它引入了一个变量pos来记录当前已经处理到的最远位置(即已经处理过的位置能够到达的最右端),从而避免重复处理。

       这段代码中利用pos来记录当前已经处理过的最远位置。在以后每次处理中,只处理新出现的区间(pos,x+k[x]),避免重复遍历。

#include <bits/stdc++.h>
//#define int long long
using namespace std;
void ooo(int x){cout<<x<<'\n';
}
struct p{int id, num;
};
const int xmmm=2e5+10;
int a[xmmm], b[xmmm], k[xmmm];
int dis[xmmm];
bool vis[xmmm];
signed main()
{int n;cin>>n;for(int i=1;i<=n;i++){cin>>a[i];b[i]=i-a[i];}for(int i=1;i<=n;i++)cin>>k[i];int pos=0;queue<p>q;//q.clear();q.push(p{1, 0});int ans=-1;while(!q.empty()){p t=q.front();q.pop();if(vis[t.id])continue;vis[t.id]=1;if(t.id==n){ans=t.num;break;}int x=t.id;if(x+k[x]<=pos)continue;if(x+k[x]>=n){q.push(p{n, t.num+1});continue;}for(int i=max(pos+1, x+1);i<=min(n, x+k[x]);i++){q.push(p{b[i], t.num+1});}pos=max(pos, x+k[x]);}cout<<ans<<'\n';return 0;
}
/*5
0 1 2 3 0
3 4 4 10 1510
0 1 1 1 1 3 1 0 3 0
2 4 5 4 1 4 1 3 5 3
*/

   借这个思路,我们在原有代码基础上进行优化,引入max_covered变量,也就是上述代码中的pos。只需要修改一点点,降低算法复杂度:

#include <bits/stdc++.h>
using namespace std;
const long long N=1e5 + 2;
int main()
{int n;scanf("%d", &n);int a[N];int k[N];vector<int> step(n + 1, -1);step[1] = 0;queue<int> q;q.push(1); //先把第一个位置push进去int max_covered = 0;for (int i = 1; i <= n; i++){cin>>a[i];}for (int i = 1; i <= n; i++){cin>>k[i];}while (!q.empty()){int x = q.front();q.pop();if (x == n) break; //已经到达终点n,可以提前跳出,可以避免超时?int l = x + 1;  //跳跃区间最左侧int r = min(n, x + k[x]);   //跳跃区间最右侧if (r == n) {step[n] = step[x] + 1;break;}if (r <= max_covered) continue;for (int i = r; i >= max(l,max_covered); i--)   //从右往左遍历{int next = i - a[i];             //注意还需要往回退if (step[next] == -1)  //说明还没有遍历到{step[next] = step[x] + 1;q.push(next);       //注意是在这里push的,而不是在for循环里push}                            //注意BFS的特点,一旦遍历到了某个节点,这个节点的距离就是最小的,定死了。}max_covered = r;         }cout << step[n];return 0;
}

成功通过:

第36次CCF认证-第二题

    这道题我记得,当时我在考场上推了半天来着,最后好像是拿到了80的部分分。

原文件:TUOJ

还是参考这个博主的题解:第36次ccf-csp题解(思维) - devoteeing - 博客园

好吧,看到“负数”这个概念让我想起来这道题笔者上一篇帖子有复习过,但是隔得时间太久了,忘记了┭┮﹏┭┮。我再去复习一下。

ps:之前没有提交题解到系统,这次发现居然这个题解也超时了,笔者当时就是因为超时扣的20呜呜呜,看来还是得看一下别的题解。

 这个大佬的题解能通过:第36次ccf-csp题解(思维) - devoteeing - 博客园

   关键在于怎样维护bi=0时,全程中所拥有补给的最小量(为负数表示还需要多少补给) 所以就是一个单点修改,以bi为分界处,维护前半段和后半段最小值,对两段最小值取最小值,为负数则取绝对值,为非负数则为0。注意后半段的值还需要带上b[i],然后再对前半段和后半段进行比较。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int xmmm=2e5+10;
int a[xmmm], b[xmmm];
int c[xmmm];
int sum[xmmm];
int pmi[xmmm], lmi[xmmm];
int ans[xmmm];
void ooo(int x){cout<<x<<'\n';
}
signed main()
{int n;cin>>n;for(int i=0;i<=n;i++){cin>>a[i];c[i*2+1]=0-a[i];}for(int i=1;i<=n;i++){cin>>b[i];c[i*2]=b[i];}pmi[0]=lmi[2*n+2]=0-1e10;for(int i=1;i<=2*n+1;i++){sum[i]=sum[i-1]+c[i];}for(int i=1;i<=2*n+1;i++){if(i==1)pmi[i]=sum[i];else pmi[i]=min(pmi[i-1], sum[i]);}for(int i=2*n+1;i>=1;i--){if(i==2*n+1)lmi[i]=sum[i];else lmi[i]=min(lmi[i+1], sum[i]);}for(int i=1;i<=n;i++){int pos=i*2;int t1=pmi[pos-1];int t2=lmi[pos]-b[i];ans[i]=min(t1, t2);}for(int i=1;i<=n;i++){if(ans[i]<0)cout<<0-ans[i]<<' ';else cout<<0<<' ';}return 0;
}
/*3
5 5 5 5
0 100 039 4 6 2
9 4 6
*/

     看完这篇题解我在思考,有没有可能将最开始那篇题解做一个优化,引入前缀和,能否解决超时的问题?应该是不行的,如果保留之前那个思路,遍历每一个b[i]依次再进行处理,应该是会超时的。

考试考点

再次看一下考试要考什么。第五题直接不看哈哈。


http://www.hkcw.cn/article/CjVZmbsFbL.shtml

相关文章

吴欣鸿:重生之我在美图做CEO 从工具自卑到工具自信

吴欣鸿:重生之我在美图做CEO 从工具自卑到工具自信!不切实际的野心和存在局限的认知,会摧毁一家公司。2018 年到 2019 年,中国互联网一派欣欣向荣,而巨亏中的美图被迫结束了它的多元化扩张,逐步放弃了手机、短视频、电商,并且快速裁掉了 60% 的员工。美图不得不退回厦门…

长尾关键词布局与SEO实战策略

内容概要 长尾关键词布局是SEO策略中撬动精准流量的核心支点&#xff0c;其本质在于通过细分需求场景捕捉用户的隐性搜索意图。与传统短词相比&#xff0c;长尾词具有流量精准度高、竞争压力小的天然优势&#xff0c;能够有效填补网站流量结构的“长尾空白区”。要实现系统性布…

Spring Cloud 开发入门:环境搭建与微服务项目实战

一、开发环境搭建 1. JDK 安装与版本选择 版本选择解析 Java 是 Spring Cloud 微服务开发的基础&#xff0c;选择合适的 JDK 版本至关重要&#xff0c;特别是在框架兼容性和生产环境稳定性方面。 &#xff08;1&#xff09;主流 JDK 版本对比 版本发布年份支持状态特点简述J…

媒体追问蒋雨融与绿发会谁在说谎 真相待解疑云重重

媒体追问蒋雨融与绿发会谁在说谎 真相待解疑云重重!最近两天,哈佛女孩蒋雨融频繁登上热搜。她在一次特殊时刻的演讲中提到“去理解、去共情一个完全在你世界之外的人”,但公众的关注点却集中在她的身世上。网友们发现,蒋雨融的父亲曾是绿发会的主任,她去哈佛读书时,绿发会…

男子提醒狗主人牵绳被打伤 遛狗纠纷升级为刑事案件

男子提醒狗主人牵绳被打伤 遛狗纠纷升级为刑事案件!重庆的刘先生反映,2月24日晚,他在小区质问两名女子遛狗为何不牵绳,却被其中一名女子打成轻伤二级。刘先生报警后,警方拟刑事立案,但后来女子取保候审,这让刘先生难以接受。6月2日,刘先生介绍,当晚他牵着朋友的狗准备…

华熙生物回应近期风波 反对名称游戏

华熙生物方面指出,胶原蛋白与“重组的”胶原蛋白是两个不同的概念。“重组的”胶原蛋白在多数情况下仍然是多肽,并不能真正重组为胶原蛋白。华熙生物反对的是这种名称游戏,而不是胶原蛋白的科研和产业转化。端午假期期间,华熙生物与巨子生物两大医美巨头再次发文喊话,措辞…

浙江端午接待游客1651.7万 亲子游成新引擎

今年端午假期与“六一”国际儿童节恰好相遇,浙江各地推出了丰富多样的活动,为来浙游客提供了多种游玩选择。看龙舟、逛古镇等体验特色民俗游成为热门选项,家庭亲子出游也成为推动客流增长的重要因素。据浙江省文化广电和旅游厅消息,端午假期三天内,这些活动吸引了大量游客…

上海一公寓被2亿元收购 核心资产易主

上海一公寓被2亿元收购 核心资产易主!中骏集团旗下位于上海核心区的中骏天悦方隅公寓被上海本土企业以2亿元收购,交易方式为股权转让。该公寓位于上海中环真如城市副中心固川路166弄,交通便利,周围总部办公楼众多,主要客群为高净值商务人士。公寓建筑面积7457平方米,是海…

zynq switch级联多个ssd方案设计(ECAM BUG修改)

本文讲解采用zynq7045芯片如何实现200T容量高速存储方案设计&#xff0c;对于大容量高速存储卡&#xff0c;首先会想到采用pcie switch级联方式&#xff0c;因为单张ssd的容量是有限制的&#xff08;目前常见的m.2接口容量为4TB&#xff0c;U.2接口容量为16TB&#xff09;&…

数字孪生智慧水利解决方案:数字化场景、智慧化模拟、精准化决策,构建数字孪生流域为核心的智慧水利体系

数字孪生水利建设是推进智慧水利建设的实施措施&#xff0c;水利部近几年先后从行政、规划、工作、技术四个层面制定印发了系列文件&#xff0c;明确了推进数字孪生水利建设的时间表、路线图、任务书、责任单&#xff0c; 完成了顶层设计。 相关政策要求按照 “需求牵引、应用至…

设计模式——访问者设计模式(行为型)

摘要 访问者设计模式是一种行为型设计模式&#xff0c;它将数据结构与作用于结构上的操作解耦&#xff0c;允许在不修改数据结构的前提下增加新的操作行为。该模式包含关键角色如元素接口、具体元素类、访问者接口和具体访问者类。通过访问者模式&#xff0c;可以在不改变对象…

曾怒怼张国伟的北体1米84校花,跳高女神胡麟鹏完成蜕变

初听她的名字,这是男的吧!拿出来手机一搜,满屏的大长腿都要溢出屏幕了,我确信了,是女的!再一看,超高的颜值,高挑的身材,这是模特吧?不不不她是田径运动员,她就是跳高冠军胡麟鹏!她1995年出生在河北,父亲是全国十项全能选手,十项全能是田径运动中全能运动项目的一…

尹锡悦夫妇现身投票站 完成总统选举投票

韩国第21届总统选举的正式投票于当地时间6月3日早上6时开始。当天上午,前总统尹锡悦及其夫人金建希前往首尔市瑞草区的一个投票站完成了投票。4月4日,韩国宪法法院通过了对总统尹锡悦的弹劾裁决,罢免了他的总统职务。根据法律规定,韩国必须在60日内举行新一届总统选举。选举…

女子路上持械打砸多辆车被逮捕 视频曝光引发热议

6月3日上午,一段关于“女子站在车辆引擎盖上持械打砸挡风玻璃”的视频在网络上引发关注。视频显示,在一个十字路口等待左转的新能源轿车上,一名女子站在引擎盖上,用器械击打了挡风玻璃两次。随后,轿车起步左转,女子仰面被顶在挡风玻璃上前行。她还随机击打了另外两辆车,…

警方通报15岁男孩遭7人殴打 因误会引发冲突

四川古蔺县警方于6月2日通报了一起涉及未成年人的暴力事件。据报道,两名未成年人在骑车时发出笑声,被15岁的陈某甲误以为是在嘲笑自己。随后,陈某甲与其他六人一起在地下停车场对这两名未成年人进行了殴打。目前,涉案的七人均已被警方抓获,其中两人因涉嫌犯罪已被刑事拘留…

山西民间网红艺人大刚离世 因酒精中毒不幸去世

山西民间网红艺人大刚离世 因酒精中毒不幸去世。6月1日,短视频账号“大刚演艺传媒”发布讣告称,景旭刚因病不幸离世,享年48岁,定于6月4日出殡。许多网友在评论区表达了对景旭刚的哀思,称赞他多才多艺。次日,景旭刚的妻子确认了这一消息,并透露他是因饮酒过量导致酒精中毒…

印度婆罗门家庭的高消费晚餐 奢华盛宴令人惊叹

印度婆罗门家庭的高消费晚餐 奢华盛宴令人惊叹!宝子们!今天带你们大开眼界,看看印度富人的家庭晚餐到底有多绝!这哪里是吃饭,简直是一场奢华的视觉与味觉盛宴,贫穷真的限制了我的想象力!本以为印度菜就是咖喱、飞饼?大错特错!在印度富人餐桌上,传统美食直接“华丽变身…

<el-date-picker>配置禁用指定日期之前的时间选择(Vue2、Vue3包括时分秒)

今天突然接受到一个离谱的需求&#xff1a;有一个需要配置定时任务开始执行时间的组件&#xff0c;之前的做法都是用<el-form>的rules定义校验规则&#xff0c;也能实现效果&#xff0c;但是今天产品突发奇想&#xff1a;不能选的时间就置灰&#xff08;就是我们说的禁用…

女孩绿灯过马路被骑电瓶大爷撞倒 女孩爸爸:对方上门道歉,让删除视频自己拒绝了

女孩绿灯过马路时被骑电瓶大爷撞倒,事后大爷走了,女孩爸爸:孩子腿受伤问题不大,后面对方上门道歉,让删除视频自己拒绝了。责任编辑:zx0002

小埃梅里晒与欧冠奖杯合照 永恒的荣耀

在欧冠决赛中,扎伊尔-埃梅里替补出场,帮助巴黎圣日耳曼以5-0战胜国际米兰,赢得了冠军。赛后,扎伊尔-埃梅里在社交媒体上分享了自己与奖杯的合照,并写道:“永恒的荣耀!很自豪将这座期待已久的奖杯带回家,作为一个巴黎人尤其如此。”他还表达了对与球迷共同庆祝胜利的期待…