题解:洛谷 P7994 Air Cownditioning
【题目来源】
洛谷:P7994 [USACO21DEC] Air Cownditioning B - 洛谷
【题目描述】
Farmer John 的 \(N\) 头奶牛对他们牛棚的室温非常挑剔。有些奶牛喜欢温度低一些,而有些奶牛则喜欢温度高一些。
Farmer John 的牛棚包含一排 \(N\) 个牛栏,编号为 \(1\dots N\),每个牛栏里有一头牛。第 \(i\) 头奶牛希望她的牛栏中的温度是 \(p_i\),而现在她的牛栏中的温度是\(t_i\)。为了确保每头奶牛都感到舒适,Farmer John 安装了一个新的空调系统。该系统进行控制的方式非常有趣,他可以向系统发送命令,告诉它将一组连续的牛栏内的温度升高或降低 \(1\) 个单位——例如「将牛栏\(5\dots 8\) 的温度升高 \(1\) 个单位」。一组连续的牛栏最短可以仅包含一个牛栏。
请帮助 Farmer John 求出他需要向新的空调系统发送的命令的最小数量,使得每头奶牛的牛栏都处于其中的奶牛的理想温度。
【输入】
输入的第一行包含 \(N\)。下一行包含 \(N\) 个非负整数 \(p_1\dots p_N\),用空格分隔。最后一行包含 \(N\) 个非负整数 \(t_1\dots t_N\)。
【输出】
输出一个整数,为 Farmer John 需要使用的最小指令数量。
【输入样例】
5
1 5 3 3 4
1 2 2 2 1
【输出样例】
5
【核心思想】
-
问题分析:给定目标温度 \(p[1..N]\) 和当前温度 \(t[1..N]\),每次操作可将一段连续区间的温度同时 \(+1\) 或 \(-1\)。求使所有牛栏达到目标温度的最小操作次数。本质是一个区间加减操作的最小次数问题,等价于将差分数组通过区间操作归零的最少步数。
-
算法选择:
- 贪心 + 差分思想:计算每个位置需要调整的温差 \(a[i] = p[i] - t[i]\),问题转化为用最少次数的连续区间操作使 \(a[1..N]\) 全部变为 \(0\)。关键在于发现相邻位置温差的"坡度"决定了额外操作数。
-
关键步骤:
- 计算温差数组:\(a[i] = p[i] - t[i]\),正数表示需要升温,负数表示需要降温
- 贪心递推(假设先处理升温部分):
- 初始化 \(f[1] = \max(a[1], 0)\)(若首项需升温则操作 \(a[1]\) 次)
- 对于 \(i\) 从 \(2\) 到 \(N\):
- 若 \(a[i-1] \geq a[i]\):\(f[i] = f[i-1]\),因为前一段的升温操作可以覆盖到当前位置(连续区间延伸)
- 若 \(a[i-1] < a[i]\):\(f[i] = f[i-1] + (a[i] - a[i-1])\),需要额外 \((a[i] - a[i-1])\) 次操作来提升当前位置的温差
- 物理意义:\(f[i]\) 记录处理到第 \(i\) 个位置时的累计操作数,每次遇到"上坡"(温差增加)就需要新增操作
- 处理降温部分:若 \(a[N] < 0\),还需额外 \(|a[N]|\) 次降温操作(整体降温)
- 输出答案:若 \(a[N] \geq 0\) 输出 \(f[N]\),否则输出 \(f[N] + |a[N]|\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),线性扫描
- 空间复杂度:\(O(N)\),存储温差数组
-
贪心处理区间加减的核心思想:
- 差分视角转化:将目标使 \(a[i]\) 全为 \(0\) 的问题,转化为用最少区间 \(+1\) 操作覆盖正数部分、区间 \(-1\) 操作覆盖负数部分
- 相邻温差决定增量:当 \(a[i] > a[i-1]\) 时,说明从位置 \(i\) 开始需要比前面更多的升温操作,这部分增量无法被前面的连续区间覆盖,必须新增 \((a[i] - a[i-1])\) 个新区间
- 下坡自然覆盖:当 \(a[i] \leq a[i-1]\) 时,前面的升温操作区间可以自然延伸覆盖到当前位置,无需额外操作
- 正负分离处理:先处理升温(正数部分),再处理降温(负数部分),两者独立计算后相加
- 适用于区间批量修改、差分数组操作最小化、连续区间覆盖类问题
【解题思路】

【算法标签】
普及 #贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, p[100005], t[100005], a[100005], f[100005];
int main()
{
cin >> n; // 输入n
for (int i=1; i<=n; i++) { // 依次输入每个牛栏希望的温度
cin >> p[i];
}
for (int i=1; i<=n; i++) { // 依次输入每个牛栏现在的温度
cin >> t[i];
}
for (int i=1; i<=n; i++) { // 计算每个牛栏距离目标还需要增加的温度(可能存在负数)
a[i] = p[i] - t[i];
}
if (a[1]>=0) f[1] = a[1]; // 默认f[1]为a[1]
else f[1] = 0; // 如果a[1]小于0,需特殊处理,f[1]为0
for (int i=2; i<=n; i++) { // 从第2个牛栏遍历至n个牛栏
if (a[i-1]>=a[i]) f[i] = f[i-1]; // 如果比前一个牛栏需要增加的温度小,那f[i]不变
else f[i] = f[i-1] + (a[i]-a[i-1]); // 否则为前一个牛栏调整温度的指令数加上i与i-1的温度差值
}
if (a[n]<0) cout << f[n]+abs(a[n]); // 如果最后一个值为负数,需要在执行a[n]次指令,使其变为0
else cout << f[n]; // 否则输出f[n](递推式的最后一个就是总的指令数)
return 0;
}
【运行结果】
5
1 5 3 3 4
1 2 2 2 1
5
浙公网安备 33010602011771号