面对新手向的暴力过题技巧-cpp
njtc-算竞队-yxy
写在前面
- 在一些类似于迷宫问题、走格子、最短路、最长路、寻路问题、树上寻宝问题等 通常会采用bfs、dfs、dp等方法。我利用一些题型来简单的使用一下过题的思想。
介绍
- tips:以下解释来自deepseek
-
当我们想在诸如树或图这类复杂数据结构中寻找特定信息时,BFS(广度优先搜索)和DFS(深度优先搜索)是两种最基础且重要的搜索策略。它们的核心区别在于访问节点的顺序。
想象一下你身处一个巨大的迷宫:
-
BFS(广度优先搜索) 就像是一支训练有素的军队,会逐层推进。它从起点开始,首先访问所有一步就能到达的地方,然后再去访问从这些地方出发两步能到达的所有新地方,以此类推。这种方式确保了你找到的路径一定是最短路径。它通常借助一个队列(Queue)来实现“先来先服务”的顺序。
-
DFS(深度优先搜索) 则像一个执着探险家,会一条路走到黑。它从起点选择一条岔路一直走下去,直到尽头,然后退回上一个岔口,尝试另一条没走过的路。这种方式优先探索的是图的“深度”,它可能会很快地进入深层分支,但找到的路径不一定是最短的。它通常借助一个栈(Stack)来实现,或者直接使用函数的递归调用来模拟这个“回溯”的过程。
简单总结一下:
-
BFS:广度优先,层层扫荡,找到的是最短路径。
-
DFS:深度优先,勇往直前,回溯另寻他路。
选择使用哪种算法取决于你的需求:如果需要最短路径或最近的结果(比如社交网络中的好友关系),BFS更合适;如果需要遍历所有可能(比如排列组合、穷举所有方案),或者树结构非常深而宽,DFS往往是更好的选择。
-
相关题目
迷宫问题(经典)
原题链接来自:(njtc-oj平台)xfxcy https://xfxcy.com/p/P0230
- 题目描述:迷宫由一个网格组成,每个单元格要么是路径(用 0 表示),要么是无法通过的墙壁(用 1 表示)。小飞侠从迷宫的左上角开始(坐标为 ( 1 , 1 ) (1,1)),目标是到达迷宫的右下角(坐标为 ( n , m ) (n,m))。 在迷宫中,小飞侠可以向上、向下、向左或向右移动。
- 输入格式:第一行包含两个整数 n n 和 m m。 接下来 n n 行,每行包含 m m 个整数( 0 0 或 1 1),表示完整的二维数组迷宫;
- 输出格式:输出一个整数,表示从左上角移动至右下角的最少移动次数;
- 数据范围:1≤n≤1000. 1≤m≤1000. 1≤m≤1000.
考虑到时间复杂度 本题无法使用dfs,所以使用bfs解决。
参考代码(bfs):
非特殊情况不推荐使用万能头,会拖慢整体的编译速度。
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <deque>
#include <unordered_map>
#include <map>
#include <cstring>
#include <cmath>
#include <unordered_set>
#include <set>
#include <utility>
#include <climits>
#include <iomanip>
#include <stack>
#include <bitset>
#define int long long
#define PII pair<int, int>
#define TLLL tuple<int , int , int>
#define INF 0x3f3f3f3f3f3f3f3f
#define all(v) v.begin() + 1 , v.end()
#define ALL(v) v.begin() , v.end()
#define endl "\n"
using namespace std;
const int N = 1e3 + 10;
int n , m;
int g[N][N] , dist[N][N];
bool st[N][N];
int dx[] = {0 , 1 , 0 , -1} , dy[] = {1 , 0 , -1 , 0}; //四个方向
void bfs(int sx , int sy)
{
queue<PII> q;
q.push({sx , sy});
st[sx][sy] = true;
while (q.size())
{
auto t = q.front();
q.pop();
int x = t.first , y = t.second;
for (int i = 0 ; i < 4 ; i ++ )
{
int xx = dx[i] + x , yy = dy[i] + y;
if (xx < 1 || xx > n || yy < 1 || yy > m || g[xx][yy]) continue;
if (st[xx][yy]) continue;
st[xx][yy] = true;
dist[xx][yy] = dist[x][y] + 1;
q.push({xx , yy});
if (xx == n && yy == m) return ;
}
}
}
void solve()
{
cin >> n >> m;
for (int i = 1 ; i <= n ; i ++ )
for (int j = 1 ; j <= m ; j ++ )
cin >> g[i][j];
bfs(1 , 1);
cout << dist[n][m] << endl;
return ;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T -- ) solve();
return 0;
}
额外的,可以使用dijkstra(无负权边的情况下) 对!没错,就是离散数学里你学过的东西!
- 解释:
Dijkstra算法是一种非常著名且高效的算法,用于在带权图中寻找从一个起始点到所有其他点的最短路径。这里的“权”通常代表了距离、成本、时间等可量化的度量。
它与BFS的核心区别在于“权”。BFS认为每走一步的成本都是相同的(可以看作权值为1),它通过“层数”来计算最短距离。而Dijkstra算法处理的是权值各不相同的图,某些路径可能边数多但总成本低,某些可能边数少但总成本高。
它的核心思想可以用一个比喻来理解:
想象你是一位城市交通指挥官,目标是计算从市中心(起始点)到城市中所有其他地点的最短行车时间。
-
初始化:你首先标记市中心的时间为0,而所有其他地点的时间暂时标记为“无穷大”,表示你还不知道如何到达那里。
-
贪心选择:你从当前已知可达的、且尚未被最终确认的地点中,选出离市中心最近的那个(也就是当前耗时最短的地点)。你确信,对于这个地点,你已经找到了绝对最短的路径。因为如果存在一条更短的路径,那它必然要经过一个耗时更长的其他地点,这显然是不可能的(前提是所有权重不能为负数)。
-
扩散更新:基于这个刚被确认的地点,你去查看从它出发的所有道路。你计算:“从市中心到这里的时间” + “从这里到下一个路口的时间”。如果这个总时间比之前记录的到达那个路口的时间更短,你就更新那个路口的记录。
-
重复:你不断地重复第2和第3步:从所有已知但未确认的地点中选出耗时最短的,确认它,然后更新它的邻居。直到所有地点都被确认,或者你找到了到目标地点的最短路径。
-
参考代码(dijkstra):
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <deque>
#include <unordered_map>
#include <map>
#include <cstring>
#include <cmath>
#include <unordered_set>
#include <set>
#include <utility>
#include <climits>
#include <iomanip>
#include <stack>
#include <bitset>
#define int long long
#define PII pair<int, int>
#define TLLL tuple<int , int , int>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f
#define all(v) v.begin() + 1 , v.end()
#define ALL(v) v.begin() , v.end()
#define endl "\n"
using namespace std;
const int N = 4e6 + 10;
int e[N] , ne[N] , h[N] , idx , dist[N];
bool st[N];
//链式前向星存图,相关概念可以参考过往文章->图论基础-透析链式前向星
inline void add(int a , int b)
{
e[idx] = b , ne[idx] = h[a] , h[a] = idx ++;
}
//将二维坐标转化为一维(即:从上到下,从左到右,依次编号为1,2,3,...,n * m)。
inline int transIndex(int a , int b , int m)
{
return (a - 1) * m + b;
}
//dijkstra
void dijkstra()
{
memset(st , false , sizeof st);
memset(dist , inf , sizeof dist);
priority_queue<PII , vector<PII> , greater<PII> > heap;
heap.push({0 , 1});
dist[1] = 0;
st[1] = true;
while (heap.size())
{
auto t = heap.top();
heap.pop();
int distance = t.first , index = t.second;
for (int i = h[index] ; ~i ; i = ne[i])
{
int j = e[i];
if (dist[j] > distance + 1)
{
dist[j] = distance + 1;
heap.push({ dist[j] , j});
}
}
}
return ;
}
//函数本体
void solve()
{
int n , m;
cin >> n >> m;
vector<vector<int> > g(n + 1 , vector<int>(m + 1));
for (int i = 1 ; i <= n ; i ++ )
for (int j = 1 ; j <= m ; j ++ )
cin >> g[i][j];
memset(h , -1 , sizeof h);
int dx[] = {0 , 1 , 0 , -1} , dy[] = {1 , 0 , -1 , 0}; //四个方向
//坐标转换
for (int i = 1 ; i <= n ; i ++ )
{
for (int j = 1 ; j <= m ; j ++ )
{
if (g[i][j]) continue;
int u = transIndex(i , j , m);
for (int k = 0 ; k < 4 ; k ++ )
{
int x = i + dx[k] , y = j + dy[k];
if (x < 1 || x > n || y < 1 || y > m || g[x][y]) continue;
int v = transIndex(x , y , m);
add(u , v) , add(v , u); //题目描述可以上下左右,即 双向图
}
}
}
dijkstra();
int end = transIndex(n , m , m);
cout << dist[end] << endl;
return ;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
持续更新中...
Comments 13 条评论
千层肚是牛的第三个胃,即瓣胃。
牛是反刍动物,有瘤胃、网胃、瓣胃和皱胃四个胃室。瓣胃的胃壁内层褶皱形成上百片叶片,且排列类似百叶窗,故瓣胃又称百叶肚,其表面呈现大量薄瓣状,带小突起。市场上的千层肚通常是牛肚经高温蒸汽加工而成,口感细腻爽脆,常用于火锅、凉拌、炒制等料理中。
@1853464363 666
“九一八事变”(又称 “奉天事变”“柳条湖事件”)是 1931 年 9 月 18 日日本蓄意制造并发动的侵华战争开端,是日本帝国主义企图以武力征服中国的重要步骤,标志着世界反法西斯战争的起点,也揭开了第二次世界大战东方战场的序幕。这一事件对中国历史、中日关系乃至世界格局都产生了深远影响,是中国近代史上不可磨灭的 “国耻日”。
一、事变背景:日本蓄谋已久的侵华计划
日本对中国东北的侵略并非偶然,而是其长期推行 “大陆政策”(妄图征服中国、称霸亚洲的扩张战略)的必然结果:
历史铺垫:1904 年日俄战争后,日本通过《朴茨茅斯和约》攫取了中国东北的 “南满铁路” 控制权,并驻军(即 “关东军”),为后续侵略奠定军事基础。
经济需求:1929 年全球经济大危机冲击日本,国内矛盾激化,日本统治集团急于通过对外侵略转移矛盾,而中国东北丰富的矿产、粮食资源成为其首要目标。
政治野心:20 世纪 30 年代,日本军部法西斯势力崛起,主张以 “武力解决满蒙问题”,逐步推翻 “币原外交”(相对温和的对华政策),确立侵略方针。
二、事变经过:一场精心策划的 “阴谋”
日本关东军为制造侵略借口,蓄意设计了 “柳条湖事件”,并以此为导火索发动全面进攻:
制造借口(1931 年 9 月 18 日夜):
关东军独立守备队第 2 大队第 3 中队中尉河本末守率部下,在沈阳北郊柳条湖村附近的南满铁路铁轨上,埋设炸药并引爆,炸毁一段铁轨和枕木。
随后,日军反诬是中国军队破坏铁路,以此为 “理由”,立即向附近的中国东北军驻地 “北大营” 发起进攻。
中国军队的 “不抵抗” 与日军快速推进:
当时东北军主力因 “中原大战” 调至关内,留守部队接到国民政府 “攘外必先安内” 的 “不抵抗命令”(核心是避免与日军冲突,集中力量 “围剿” 红军),北大营守军几乎未作有效抵抗便奉命撤退。
9 月 19 日清晨,日军轻松占领沈阳城,随后分兵进攻东北各地:至 9 月 22 日,占领长春、四平、营口等重要城市;11 月占领黑龙江省会齐齐哈尔;1932 年 1 月占领锦州;2 月,东北全境(约 128 万平方公里,相当于日本本土 3.5 倍)沦陷。
三、事变后果:中国东北的深重灾难
伪 “满洲国” 的建立:1932 年 3 月,日本扶植末代皇帝溥仪,在长春建立傀儡政权 “满洲国”,将东北变为日本的 “殖民地”,开始了长达 14 年的殖民统治。
经济掠夺与资源浩劫:日本通过 “满铁”“满业” 等机构,垄断东北的煤炭、钢铁、粮食等资源,疯狂掠夺财富,仅煤炭一项,14 年间就被掠夺约 2.2 亿吨,大量中国劳工死于非人的劳动条件(如 “万人坑”)。
文化奴役与思想控制:日本在东北推行 “奴化教育”,禁止中国学生学习中国历史、文化,强制学习日语和 “皇道思想”,妄图磨灭中国人民的民族意识;同时实施残酷的 “治安肃正”,镇压反抗运动,制造了无数惨案(如 “平顶山惨案”,1932 年 9 月,日军屠杀抚顺平顶山村民 3000 余人)。
四、历史影响:唤醒民族觉醒,改变中国命运
民族意识的空前觉醒:九一八事变打破了国民政府 “攘外必先安内” 的幻想,也让中国人民认清了日本的侵略本质。各地学生、工人、市民纷纷举行抗日游行、罢课、罢工,呼吁 “停止内战,一致抗日”,抗日救亡运动迅速席卷全国。
中国局部抗战的开始:虽然国民政府推行 “不抵抗”,但东北人民并未屈服 —— 东北军部分爱国官兵(如马占山率部在江桥抗战)、农民武装(如 “大刀会”“红枪会”)以及中国共产党领导的抗日游击队(后发展为东北抗日联军),在东北开展了艰苦卓绝的游击战争,成为中国抗战的 “先声”。
世界反法西斯战争的起点:九一八事变是日本对外侵略扩张的开端,也是法西斯势力在全球范围内发动侵略的最早事件,比 1939 年德国入侵波兰早 8 年,因此被国际社会视为 “世界反法西斯战争的起点”,中国也成为最早进行反法西斯战争的国家。
五、历史启示:铭记国耻,守护和平
警惕历史虚无主义:近年来,日本部分右翼势力试图淡化、否认九一八事变的侵略性质(如称其为 “自卫事件”“满洲事变”),这种行为是对历史事实的背叛,也是对中国人民感情的伤害。我们必须以史料为依据,坚决驳斥历史虚无主义,维护历史真相。
以史为鉴,自强自立:九一八事变的惨痛教训证明,“落后就要挨打”。只有国家强大、民族团结,才能抵御外来侵略。如今的中国需在经济、科技、国防等领域不断发展,以实力守护和平。
传承抗日精神,珍爱和平:东北抗日联军等先烈用生命诠释了 “宁死不屈、血战到底” 的民族精神,这种精神是中华民族的宝贵财富。铭记九一八,不是为了延续仇恨,而是为了警惕战争、守护和平,避免历史悲剧重演。
每年 9 月 18 日,中国许多城市都会拉响防空警报,以纪念这一历史时刻,提醒全体中国人民:勿忘国耻,振兴中华。
Gradient Theme
@1853464363 1111
Valaxy 和 Mizuki 都是开源静态博客框架,前者基于 Vue 3、Vite 2 等技术,后者基于 Astro 框架,两者各有优势,难以直接评判哪个更好,具体取决于用户的需求和技术背景。以下是它们的主要特点对比:
Valaxy
技术栈先进:基于 Vue 3、Vite 2、pnpm 和 ESBuild 构建,具有闪电般的开发速度,支持页面和配置的热重载,能为开发者提供流畅的开发体验。
语法支持全面:专注于 Markdown 写作,支持完整的 Markdown 语法及各种扩展,方便用户专注于内容创作。
定制性高:允许用户在 Markdown 中使用 Vue,可自定义任何组件和布局,还能通过 UnoCSS 自由编写样式,满足个性化需求。
部署便捷:内置了 GitHub Actions、Netlify、Vercel 等平台的部署配置,实现一键部署,降低了部署难度。
国际化支持:借助 Vue i18n + CSS i18n,无缝支持单页和本地元素的全球多语言,无需新建页面或站点。
Mizuki
过渡动画独特:添加了全新的过渡动画组件,能为博客带来更丰富的视觉效果,提升用户体验。
基于 Astro 框架:继承了 Astro 框架的优势,如性能优化、组件化开发等,能够构建出高效、可维护的博客系统。
多平台开源:在 GitHub 和 Gitee 上都有开源代码,方便用户获取和贡献,同时也有详细的项目文档和演示网站,便于用户了解和使用。
主题美观:作为 Astro 的主题之一,Mizuki 具有简洁美观的界面设计,且可能具备良好的响应式布局,适应不同设备的浏览需求。
如果用户熟悉 Vue 技术栈,追求快速开发和高度定制化,并且希望有便捷的部署方式和良好的国际化支持,那么 Valaxy 可能是更好的选择;如果用户喜欢独特的过渡动画效果,对 Astro 框架感兴趣,或者更倾向于使用多平台开源的项目,那么 Mizuki 可能更适合。
根据相关计算和分析,当电路建立时间
s
远大于
(k−1)
b
p
时,分组交换的时延比电路交换的要小。
假设从源点到终点共经过
k
段链路,每段链路的传播时延为
d
(
s
),数据率为
b
(
b/s
),在电路交换时电路的建立时间为
s
(
s
),要传送的报文共
x
(
bit
),在分组交换时分组长度为
p
(
bit
),且各结点的排队等待时间可忽略不计。
此时,电路交换时延为
kd+
b
x
+s
,分组交换时延为
kd+(
p
x
)
b
p
+(k−1)
b
p
,其中
(k−1)
b
p
表示
k
段传输中,有
(k−1)
次的存储转发延迟。当
s
远大于
(k−1)
b
p
时,分组交换的时延小于电路交换的时延。
此外,如果报文长度
x
远大于分组长度
p
,分组交换相对电路交换在时延上的优势也会更明显。因为此时分组交换中存储转发带来的额外延迟相对较小,而电路交换的建立时间
s
成为影响时延的主要因素。
≫
在数学和计算机科学中,表示远大于的符号通常使用“≫”来表示。 这个符号被称为“远大于”或“远超过”符号,用于表明一个数值远远大于另一个数值。 例如,如果我们想表达A远大于B,我们可以写成A ≫ B。 这意味着A的值显著地大于B的值,这种差异可能是数量级上的。 在不同的编程语言和数学软件中,这个符号的具体实现方式可能会有所不同。 在一些情况下,可能需要使用特定的代码或命令来输入或显示这个符号。
orz
@1853464363 确实
@2797435460 好的好的
( ̄▽ ̄)(⌒▽⌒)%%%%%%%%%%%