题解:洛谷 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

【核心思想】

  1. 问题分析:给定目标温度 \(p[1..N]\) 和当前温度 \(t[1..N]\),每次操作可将一段连续区间的温度同时 \(+1\)\(-1\)。求使所有牛栏达到目标温度的最小操作次数。本质是一个区间加减操作的最小次数问题,等价于将差分数组通过区间操作归零的最少步数。

  2. 算法选择

    • 贪心 + 差分思想:计算每个位置需要调整的温差 \(a[i] = p[i] - t[i]\),问题转化为用最少次数的连续区间操作使 \(a[1..N]\) 全部变为 \(0\)。关键在于发现相邻位置温差的"坡度"决定了额外操作数。
  3. 关键步骤

    • 计算温差数组\(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]|\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N)\),线性扫描
    • 空间复杂度:\(O(N)\),存储温差数组
  5. 贪心处理区间加减的核心思想

    • 差分视角转化:将目标使 \(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
posted @ 2026-08-29 09:39  团爸讲算法  阅读(4)  评论(0)    收藏  举报