寒假集训专题五:搜索

news/2025/2/13 20:10:39/文章来源:https://www.cnblogs.com/yeqa113/p/18712394

ESAY1:自然数的拆分

P2404 自然数的拆分问题

题目描述

任何一个大于 \(1\) 的自然数 \(n\),总可以拆分成若干个小于 \(n\) 的自然数之和。现在给你一个自然数 \(n\),要求你求出 \(n\) 的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列,其中字典序小的序列需要优先输出。

输入格式

输入:待拆分的自然数 \(n\)

输出格式

输出:若干数的加法式子。

输入输出样例 #1

输入 #1

7

输出 #1

1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+1+3
1+1+1+2+2
1+1+1+4
1+1+2+3
1+1+5
1+2+2+2
1+2+4
1+3+3
1+6
2+2+3
2+5
3+4

说明/提示

数据保证,\(2\leq n\le 8\)

解题思路:
这种有点类似于全排列这种类型,需要枚举每一种有可能的组合,我们就会使用dfs来进行每一种组合的筛选。
首先我们需要一个数组来存储当前已选择的数字,以及记录当前选择的数字和所剩的余额。
我们观察样例输出的形式,所以我们从1开始进行搜索,搜索一直进行到全选1输出完,然后往回退一步,回到选出倒二个1的时候选用2.
以此类推,我们再dfs中需要遍历当前选用数字到剩余数字全部的结果。
终止条件的判断,因为我们在遍历中将选择数字大小约束在<=rest的范围内,所以再次调用时rest一定不会出现负数情况,当rest不等于0时只能说明当前数字选择不能够分解给定有理数,我们直接判断rest是否等于0就行。
最后是考虑特殊状况,我们注意到样例输出时,没有出现要被拆分的有理数本身,所以我们将满足rest==0但是数组大小仅有1的情况排除。

#include<bits/stdc++.h>
using namespace std;void dfs(vector<int>& prim, int rest, int cur_num) {if (rest == 0) {if( prim.size() == 1 ){return;}else{// 输出当前分解式for( int i = 0; i < prim.size(); i++ ){cout << prim[i]	 << ( i == prim.size()-1 ? "\n" : "+" );} return;}}// 尝试所有可能的数字for (int i = cur_num; i <= rest; i++) {prim.push_back(i);dfs(prim, rest - i, i); // 递归调用prim.pop_back(); // 回溯}
}int main() {vector<int> prim;int n;cin >> n;dfs(prim, n, 1);return 0;
}

ESAY2:填涂颜色

P1162 填涂颜色

题目描述

由数字 \(0\) 组成的方阵中,有一任意形状的由数字 \(1\) 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 \(2\)。例如:\(6\times 6\) 的方阵(\(n=6\)),涂色前和涂色后的方阵如下:

如果从某个 \(0\) 出发,只向上下左右 \(4\) 个方向移动且仅经过其他 \(0\) 的情况下,无法到达方阵的边界,就认为这个 \(0\) 在闭合圈内。闭合圈不一定是环形的,可以是任意形状,但保证闭合圈内\(0\) 是连通的(两两之间可以相互到达)。

0 0 0 0 0 0
0 0 0 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 1 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 0 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 1 2 1
1 1 1 1 1 1

输入格式

每组测试数据第一行一个整数 \(n(1 \le n \le 30)\)

接下来 \(n\) 行,由 \(0\)\(1\) 组成的 \(n \times n\) 的方阵。

方阵内只有一个闭合圈,圈内至少有一个 \(0\)

输出格式

已经填好数字 \(2\) 的完整方阵。

输入输出样例 #1

输入 #1

6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1

输出 #1

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

说明/提示

对于 \(100\%\) 的数据,\(1 \le n \le 30\)

解题思路:
这种连通块的问题就像课件里给出那道水坑问题一样,如果一块一块地调用dfs()效率低下,所以我们采用了bfs()。
这道题其实需要采用一种比较巧妙的做法,因为直接去找闭合圈中的0并不是简单的事,如果一个一个判断条件(触壁是1还是边界)真的很麻烦。我们就考虑是不是可以找闭合圈外的0呢。
但是,这时候就出现一个问题,就是bfs()在水坑问题里判断水坑数量是从连通性上来判断的,我们在闭合圈外的0区域可能不具有连通性,这样问题怎么解决?
我们可以想一下,正方形的矩阵,将外围区域扩大一整圈,然后填上0,是不是闭合圈外所有的0都联通上了,这时候我们可以从mp数组的[0][0]位置开始进行bfs()搜索,将所有在闭合圈外的0连通起来,并且在访问数组中将这些0赋值为已被访问。(记得把1也赋值成已被访问,当作墙壁避免回头)
这样就能在输出时进行判断,如果mp中是1就正常输出,如果是0就再次进行判断,如果已被访问,那就输出0,没被访问就输出2.
(也可以用dfs(),因为目的是标记不在闭合圈内的0,只要搜索之后记录就行了,就是还要把边界搜索一下,以防标记到闭合圈内的0)

#include<bits/stdc++.h>
using namespace std;const int maxn = 32;
int n;vector<vector<int>> mp( maxn, vector<int>( maxn, 0 ) );
vector<vector<bool>> vis( maxn, vector<bool>( maxn, false ) );void bfs( int i, int j )
{	if( i < 0 || i > n+1 || j < 0 || j > n+1 || vis[i][j] ){return;}vis[i][j] = true;vector<vector<int>> directions = { { -1, 0 }, { 0, -1 }, { 0, 1 }, { 1, 0 } };for( auto dir : directions ){bfs( i + dir[0], j + dir[1] );}
}int main()
{cin >> n;for( int i = 1 ; i <= n; i++ ){for( int j = 1; j <= n; j++ ){cin >> mp[i][j];if( mp[i][j] == 1 ){vis[i][j] = true;}}}bfs(0, 0);for( int i = 1 ; i <= n; i++ ){for( int j = 1; j <= n; j++ ){if( mp[i][j] == 1 ){cout << mp[i][j] << ( j == n ? "\n" : " " );}else{if( vis[i][j] ){cout << 0 << ( j == n ? "\n" : " " );}else{cout << 2 << ( j == n ? "\n" : " " );}}}}return 0; 
}

MEDIUM1:显示图像

P1256 显示图像

题目描述

古老的显示屏是由 \(N \times M\) 个像素(Pixel)点组成的。一个像素点的位置是根据所在行数和列数决定的。例如 \(P(2,1)\) 表示第 \(2\) 行第 \(1\) 列的像素点。那时候,屏幕只能显示黑与白两种颜色,人们用二进制 \(0\)\(1\) 来表示。\(0\) 表示黑色,\(1\) 表示白色。当计算机发出一个指令:\(P(x,y)=1\),则屏幕上的第 \(x\) 行第 \(y\) 列的阴极射线管就开始工作,使该像素点显示白色,若 \(P(x,y)=0\),则对应位置的阴极射线管不工作,像素点保持黑色。在某一单位时刻,计算机以 \(N \times M\) 二维 \(01\) 矩阵的方式发出显示整个屏幕图像的命令。

例如,屏幕是由 \(3 \times 4\) 的像素点组成,在某单位时刻,计算机发出如下命令:

\[\begin{pmatrix} 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 1 \\ 0 & 1 & 1 & 0 \\ \end{pmatrix}\]

对应屏幕显示应为:

假设放大后,一个格子表示一个像素点。

由于未知的原因,显示黑色的像素点总是受显示白色的像素点的影响——可能是阴极射线管工作的作用。并且,距离越近,影响越大。这里的距离定义如下:

设有像素点 \(P_1(x_1,y_1)\) 和像素点 \(P_2(x_2,y_2)\),则它们之间的距离 \(D(P_1,P_2)=|x_1-x_2|+|y_1-y_2|\)

在某一时刻,计算机发出显示命令后,科学家们期望知道,每个像素点和其最近的显示白色的像素点之间的最短距离是多少——科学家们保证屏幕上至少有一个显示白色的像素点。

上面的例子中,像素 \(P(1,1)\) 与最近的白色像素点之间的距离为 \(3\),而像素 \(P(3,2)\) 本身显示白色,所以最短距离为 \(0\)

输入格式

第一行有两个数字,\(N\)\(M\ (1 \le N,M \le 182)\),表示屏幕的规格。

以下 \(N\) 行,每行 \(M\) 个数字,\(0\)\(1\)。为计算机发出的显示命令。

输出格式

输出文件有 \(N\) 行,每行 \(M\) 个数字,中间用 \(1\) 个空格分开。第 \(i\) 行第 \(j\) 列的数字表示距像素点 \(P(i,j)\) 最近的白色像素点的最短距离。

输入输出样例 #1

输入 #1

3 4
0001
0011
0110

输出 #1

3 2 1 0
2 1 0 0
1 0 0 1

说明/提示

  • 对于 \(30\%\) 的数据:\(N\times M \le 10000\)
  • 对于 \(100\%\) 的数据:\(N\times M \le 182^2\)

解题思路:
这道题有这类最短路径的要求,我们就可以考虑运用bfs()函数进行搜索。
但是这道题从黑色块开始搜索,我们可能要调用多次函数,所以我们换个思路,我们可以从白色显示块开始,对黑色块进行搜索。
在搜索途中对搜索过的黑色块进行标记,记录下当前是搜索的第几步,记录下当作白色快到该色块的距离。
本质上是把搜索过的黑色块记录距离后当成白色块遍历搜索。
要注意输入时01是连起来的,我选用的是按字符串处理,也可以按字符单个输入。

#include<bits/stdc++.h> 
using namespace std;const int maxn = 182;vector<vector<int>> mp;
vector<vector<bool>> vis( maxn, vector<bool>( maxn, false ) );
vector<vector<int>> dis( maxn, vector<int>( maxn, -1 ) );
vector<vector<int>> directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };int n, m;    void bfs()
{queue<pair<int,int>> q;for( int i = 0; i < n; i++ ){for( int j = 0; j < m; j++ ){if( mp[i][j] == 1 ){dis[i][j] = 0;vis[i][j] = true;q.push( { i, j } );}}}while( !q.empty() ){auto node = q.front();q.pop();for( auto& dir : directions ){int ni = node.first + dir[0];int nj = node.second + dir[1];if( ni >= 0 && ni < n && nj >= 0 && nj < m && !vis[ni][nj] ){dis[ni][nj] = dis[node.first][node.second] + 1;vis[ni][nj] = true;q.push( { ni, nj } );}}}}int main()
{cin >> n >> m;mp.resize( n, vector<int>( m ) );queue<pair<int,int>> q;for( int i = 0; i < n; i++ ){string s;cin >> s;for( int j = 0; j < m; j++ ){mp[i][j] = s[j] - '0';}}bfs();for( int i = 0; i < n; i++ ){for( int j = 0; j < m; j++ ){cout << dis[i][j] << ( j == m - 1 ? "\n" : " " );}}return 0;
}

MEDIUM2:健康的荷斯坦奶牛

P1460 [USACO2.1] 健康的荷斯坦奶牛 Healthy Holsteins

题目描述

农民 John 以拥有世界上最健康的奶牛为傲。他知道每种饲料中所包含的牛所需的最低的维他命量是多少。请你帮助农夫喂养他的牛,以保持它们的健康,使喂给牛的饲料的种数最少。

给出牛所需的最低的维他命量,输出喂给牛需要哪些种类的饲料,且所需的饲料剂量最少。

维他命量以整数表示,每种饲料最多只能对牛使用一次,数据保证存在解。

输入格式

第一行一个整数 \(v\),表示需要的维他命的种类数。
第二行 \(v\) 个整数,表示牛每天需要的每种维他命的最小量。

第三行一个整数 \(g\),表示可用来喂牛的饲料的种数。
下面 \(g\) 行,第 \(n\) 行表示编号为 \(n\) 饲料包含的各种维他命的量的多少。

输出格式

输出文件只有一行,包括牛必需的最小的饲料种数 \(p\);后面有 \(p\) 个数,表示所选择的饲料编号(按从小到大排列)。

如果有多个解,输出饲料序号最小的(即字典序最小)。

输入输出样例 #1

输入 #1

4
100 200 300 400
3
50  50  50  50
200 300 200 300
900 150 389 399

输出 #1

2 1 3

说明/提示

【数据范围】
对于 \(100\%\) 的数据,\(1\le v \le 25\)\(1\le g \le 15\)
输入的所有整数在 \([1,1000]\) 范围内。

USACO 2.1

翻译来自NOCOW

解题思路:
是要求输出组合的题目,我们就会容易想到运用dfs()来解题。
首先分析一下我们需要存储奶牛的需求,还有饲料的信息,以及存储答案的临时数组和最终答案的数组,最后我都选用vector。
然后,考虑我们dfs()递归调用的终止条件,就是当我们选用的饲料,每一项都达到需求。除此之外,还要考虑是否是最小的饲料种类。
做一个大小判断,如果更小就直接替换掉答案,这里可能会考虑很多什么后面提到的字典序的大小问题,c++里面可以直接比较大小,不过这里因为递归调用后出现的最早答案一定是最小字典序的,所以这里不判断也是可以的。

#include<bits/stdc++.h>
using namespace std;const int maxn = 1001;vector<int> ans(maxn);
vector<int> temp(maxn);
vector<int> need(maxn);
vector<vector<int>> im( maxn, vector<int>( maxn, 0 ) );
int v, g;
int minn = INT_MAX;bool check( int s )
{for( int i = 1; i <= v; i++ ){int sum = 0;for( int j = 1; j <= s; j++ ){sum += im[temp[j]][i];}if( sum < need[i] ){return false;}}return true;
}void dfs( int t, int s )
{if( t > g ){if( check(s) ){if( s < minn ){minn = s;for( int i = 1; i <= minn; i++ ){	ans[i] = temp[i];}}}return;}temp[s+1] = t;dfs( t+1, s+1 );temp[s+1] = 0;dfs( t+1, s );
}int main()
{cin >> v;for( int i = 1; i <= v; i++ ){cin >> need[i];}cin >> g;for( int i = 1; i <= g; i++ ){for( int j = 1; j <= v; j++ ){cin >> im[i][j];}}dfs( 1, 0 );cout << minn << " ";for( int i = 1; i <= minn; i++ ){cout << ans[i] << ( i == minn ? "\n" : " " );}return 0;}

学习总结:

遇到组合类问题可以考虑dfs,遇到连通体或者最短路径可以考虑bfs。

//全排列
const int maxn = 2e6+5;
bool vis[maxn];
int box[maxn], n;void put_card_into_box( int j )
{if( j == n + 1 ){for( int i = 1; i <= n; i++ ){cout << box[i];}cout << endl;return;	}for( int i = 1; i <= n; i++ )	{if( !vis[i] ){box[j] = i;vis[i] = true;put_card_into_box( j + 1 );vis[i] = false;}}
} //dfs
void dfs( int step )
{//如果到达终点,计数加一,返回 if( 符合终止条件 ){结束递归,返回}//做出一个决策,进行下一步dfs(step+1);//撤回这个决策,进行下一步dfs(step+1);}//bfs
void bfs()queue q;//初始状态入队while( !q.empty() )
{//队首出队,if( 判断符合条件或到达边界 ) return;//由队首拓展新状态if(新状态合理){//新状态入队}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/883382.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多模态 AI 怎么玩?这里有 18 个脑洞

在 RTE 开发者社区,我们会和大家一起探索全球最前沿的 Real-Time AI 技术,和最有想法的新兴场景。Google 近期举办了一场名为「MultiModal Hackathon」的限时编程活动,聚焦于 多模态与 Gemini 2.0 的最新能力。活动汇聚了 200 多位开发者,共同探索多模态 AI、实时 AI、生成…

Svelte 最新中文文档翻译(7)—— snippet 与 @render

前言 Svelte,一个非常“有趣”、用起来“很爽”的前端框架。从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1:Svelte 以其独特的编译时优化机制著称,具有轻量级、高性能、易上手等特性,非常适合构…

基环树 DP:学习笔记

总述 定义 基环树,是一个 \(N\) 个点和 \(N\) 条边的连通图,特征是图中有且仅有一个环。特别的,如果不连通且每个连通块的点数和边数都相等,那么这就是一个基环树森林。 基环树 DP,顾名思义,就是在一个基环树上 DP,或是 DP 的结构类似基环树。相对于正常的树型 DP,一般…

数字孪生如何让GIS场景视效瞬间高大上?带你了解鲸孪生中的GIS系统

GIS与数字孪生的结合非常紧密,而山海鲸可视化作为一个数字孪生平台,也将GIS系统整合在了鲸孪生功能之中。 GIS中包含了大量的数据,例如遥感数据、地形数据、倾斜摄影数据等,能够为数字孪生系统提供非常好的补充。同时,传统的GIS系统整体视觉效果相对较差,与其他模型和数据…

2 分支 多个思路

利用分支,你就可以在同一个代码基础上同时处理多个完全没有关联、相互独立的工作。考虑以下场景。 假设你正在改一个 Bug-A,此时已经产生了大量的代码修改,并且离修复完成还有很长一段时间(起码得明天)。此时,有一个着急但简单的 Bug-B 需要你立即完成,并在一个小时内同…

记录一种DAG计数方法与一个配套技巧

记录一种DAG计数方法与一个配套技巧 定义 \(f_S\) 表示集合 \(S\) 中的点构成的合法 DAG 子图的方案数。假设找到 DAG 中一个入度为 \(0\) 的节点 \(x\),那么很明显 \(f_S=\sum_{x}f_{S\setminus \{x\}}\),这明显要算重因为 \(S\setminus \{x\}\) 中也有入度为 \(0\) 的点。 …

野鸡题手写题解整合

浴谷正在蒸蒸日上,专栏区怕是马上要倒闭了。 CF2026F 题 题。题外话:这场有点水平,E 题让我重拾了最大权闭合子图的记忆。 首先考虑没有这个可持久化(只有 \(2,3,4\) 操作)怎么做。\(0/1\) 背包问题,动态维护当前的 dp 数组 \(f_i\) 表示总体积 \(\sum p\) 不超过 \(i\) …

鸿蒙开发:了解@Builder装饰器

@Builder装饰是鸿蒙UI开发中,非常重要的一个装饰器,在实际的开发中,合理且正确的使用,能够让我们的代码更加的简洁前言本文代码案例基于Api13,温馨提示:内容相对来说比较简单,如果您已掌握,略过即可。如果说一个页面中组件有很多,我们都统一写到build函数中,显而易见…

P1020 [NOIP 1999 提高组] 导弹拦截(dilworth)

这道题真的做的我鬼火冒,尤其是这个第二问要用到dilworth但是我看讲解完全不知道他们在讲什么,我看了好久才理解,一个数组至少可以由几个不增子序列覆盖就等于严格单调递增的最长子序列的长度,如果是至少可以由几个严格递减子序列覆盖就等于最长单调不减子序列的长度,然后…

Linux系统介绍

1. Linux介绍 Linux和windows一样也是一个操作系统,但是与windows不同的是,Linux是一套开放源码的代码程序、并且可以自由传播的类unix操作系统软件。 Linux系统主要被应用于服务端、嵌入式开发和个人PC桌面3大领域,一般的WEB项目都是部署在Linux操作系统上。 Linux是一个基…

Linux驱动---按键

文章简述了Input子系统架构,讲解了输入设备驱动开发流程,探讨了按键消抖方法及按键驱动开发要点,为嵌入式开发提供参考。目录一、Input子系统1.1、简介1.2、Input子系统构成1.3、input_dev结构体二、输入设备驱动开发流程2.1、分配和初始化输入设备2.2、注册设备2.3、事件上…