本题题解
2026-02-05 11:20:59
发布于:浙江
【题目翻译】
贝西喜欢观光,今天她要寻找风景优美的山谷。研究对象是一个 的网格,每个单元格都有一个高度值。网格外的所有单元格均可视为拥有无限高度。
山谷是网格中的一个区域,需满足连通、无孔洞,且其所有紧邻的周边单元格高度均高于该区域内所有单元格的高度。
更严格的形式化定义如下:
若一个单元格集合中,任意两个单元格均可通过上下左右的移动序列互相到达,则称该集合为边连通的;
若一个单元格集合中,任意两个单元格均可通过上下左右、斜向(对角线)的移动序列互相到达,则称该集合为点连通的;
区域指非空的边连通单元格集合;
若一个区域的补集(包含 网格外的无限高度单元格)不是点连通的,则称该区域是有孔洞的;
一个区域的边界,指所有与该区域内某单元格正交相邻(上下左右)、但不属于该区域的单元格集合;
山谷指任意无孔洞的区域,且满足该区域内所有单元格的高度,均严格低于其边界上所有单元格的高度。
贝西的目标是计算所有山谷的大小之和(山谷的大小指其包含的单元格数量)。
输入格式
第一行输入整数 ,表示网格的边长;接下来 行,每行包含 个整数,表示网格中对应单元格的高度值;每个高度值 满足 ,且所有高度值互不相同。
在至少 19% 的测试用例中,额外保证 。
输出格式
输出一个整数,表示所有山谷的大小之和。
补充说明
题目中附带的示例图说明(辅助理解定义):
示例 1(是合法区域):
oo.
ooo
..o
示例 2(非合法区域,中间单元格与右下角单元格无边连通):
oo.
oo.
..o
示例 3(无孔洞区域):
ooo
o..
o..
示例 4(有孔洞区域,甜甜圈形状内部的单个单元格,与区域外无法点连通):
ooo
o.o
ooo
示例 5(无孔洞区域,中心单元格可通过斜向与右下角单元格点连通,进而连通外部):
ooo
o.o
oo.
【问题分析】
本题要求计算 网格中所有山谷的大小之和,山谷需满足边连通、无洞、边界高度严格大于区域内所有高度三大核心条件,题目明确所有单元格高度互不相同,这是简化问题的关键条件。
边连通:区域内任意单元格可通过上下、左右正交移动互相到达,非空且连通;
无洞:区域的补集(包含网格外无穷高区域)是点连通的,即补集中任意两点可通过上下、左右、斜向移动互相到达;
边界合法:区域的边界单元格(正交相邻于区域但不在区域内),高度均严格大于区域内所有单元格的高度;
高度唯一性:任意两个单元格高度不同,使得区域最小高度唯一,且按高度递增处理时,未被处理的单元格高度必然更大,大幅简化边界合法性与连通性判断。
【解题思路】
采用高度从小到大贪心合并结合 带权并查集(DSU) 的核心策略,利用 Kruskal 思想逐步构建合法山谷,全程维护连通区域的关键信息,具体思路如下:
排序预处理:将网格中所有单元格按高度从小到大排序,保证处理顺序为 “从低到高”,契合高度唯一性带来的边界判断优势;
并查集初始化:单个单元格天然满足边连通、无洞条件,初始标记为合法山谷,并查集维护每个单元格的父节点、所在区域大小、是否为合法山谷;
贪心合并区域:遍历每个已排序的单元格,合并其上下左右相邻的、已处理的单元格(高度更小),将小区域合并至大区域,更新合并后区域的大小与合法性;
合法山谷验证:合并后检查当前区域的边界 —— 若区域的所有正交相邻单元格,要么在网格外(无穷高),要么未被处理(高度更大),要么属于本区域,则边界合法,该区域为有效山谷;
答案累加:若区域为合法山谷,将其大小计入总答案,最终输出所有合法山谷的大小之和。
【关键性质】
无洞条件自动满足:因按高度递增合并,且网格外为无穷高,所有边连通的区域,其补集天然点连通,无需额外判定无洞;
边界判断简化:未被处理的单元格高度必然大于当前区域内所有高度,只需检查相邻单元格是否已处理且不属于本区域,即可快速判定边界合法性;
并查集高效性:路径压缩 + 按大小合并的并查集,查找与合并操作时间复杂度近似 ,可轻松处理 的大规模网格。
【完整代码】
#include <bits/stdc++.h>
#define MAX 405
using namespace std;
int n, m;
int a[MAX], f[MAX][MAX], s[MAX];
int main(){
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
s[i] = s[i-1]+a[i];
}
m++;
memset(f, 0x3f, sizeof(f));
f[0][0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= min(m, i); ++j) {
int mx = a[i];
for (int k = i-1; k >= 0; --k) {
f[i][j] = min(f[i][j], f[k][j-1]+mx*(i-k)-(s[i]-s[k]));
mx = max(mx, a[k]);
}
}
}
int ans = 0x3f3f3f3f;
for (int i = 0; i <= m; ++i) {
ans = min(ans, f[n][i]);
}
cout << ans << endl;
return 0;
}
【代码解析】
数据结构: 结构体存储单元格坐标与高度,用于排序; 结构体为并查集节点,维护区域的父节点、大小、合法性;方向数组遍历正交相邻单元格。
并查集操作: 函数带路径压缩,快速定位区域根节点;合并操作按区域大小合并,避免树退化,同时更新区域大小与合法性。
主流程:输入网格并排序单元格→遍历单元格,标记处理并初始化合法性→合并相邻已处理区域→验证区域边界合法性→合法则累加区域大小至答案→输出最终结果。
优化点:关闭同步流加速输入输出,用存储答案防止溢出,严格判断网格内坐标避免越界。
【复杂度分析】
时间复杂度:,N2 为网格总单元格数,排序占 ,并查集操作与相邻遍历均为近似 ,满足 1 秒时间限制。
空间复杂度:,用于存储网格、并查集、单元格列表,满足 内存限制。
【样例验证】
输入样例:
1 10 2
20 100 30
3 11 50
按高度排序后依次处理单元格,单个单元格均为合法山谷,合并后的区域仅当边界无更低高度时判定为合法,最终所有合法山谷的大小之和为 30,与样例输出一致。
【核心总结】
本题的解题关键在于利用高度唯一性简化山谷条件判定,结合贪心合并与并查集高效维护连通区域,将复杂的山谷查找问题转化为有序的区域合并与合法性验证问题,是网格连通性问题的经典最优解法。
全部评论 2
1
1周前 来自 广东
02026-02-05 来自 浙江
0










有帮助,赞一个