题解:洛谷 P1672 Feed Accounting

【题目来源】

洛谷:P1672 [USACO05FEB] Feed Accounting S - 洛谷

【题目描述】

约翰想知道上一船饲料是什么时候运到的。在饲料运到之前,他的牛正好把仓库里原来的饲料全吃光了。他收到运来的 \(F1(1\le F1\le 10^6)\) 千克饲料。遗憾的是,他已经不记得这是哪一天的事情了。到第 \(D(1\le D\le 2\times 10^3)\) 天为止,仓库里还剩下 \(F2(1\le F2\le F1)\) 千克饲料。

约翰养了 \(C(1\le C\le 100)\) 头牛,每头牛每天都吃掉恰好 \(1\) 千克饲料。由于不同的原因,牛们从某一天开始在仓库吃饲料,又在某一天离开仓库,所以不同的两天可能会有差距很大的饲料消耗量。每头牛在来的那天和离开的那天都在仓库吃饲料。给出今天的日期 \(D\),写一个程序,判断饲料最近一次运到是在什么时候。今天牛们已经吃过饲料了,并且饲料运到的那天牛们还没有吃过饲料。

如果最终的答案有多个可能,请输出最大的(即,最近的)那一个。

【输入】

\(1\) 行:四个整数 \(C\)\(F1\)\(F2\)\(D\),用空格隔开。

\(2\)\(C+1\) 行:每行是用空格隔开的两个数字,分别表示一头牛来仓库吃饲料的时间和离开的时间。

【输出】

一个正整数,即上一船饲料运到的时间。

【输入样例】

3 14 4 10
1 9
5 8
8 12

【输出样例】

6

【核心思想】

  1. 问题分析:给定 \(C\) 头牛在仓库的进食时间段、初始饲料量 \(F1\)、第 \(D\) 天剩余量 \(F2\),求饲料最近一次运到的日期。运到当天牛尚未进食,之后每天牛按时间段吃饲料。这是一个差分数组 + 前缀和 + 逆向扫描问题,关键在于用差分统计每天的牛数量,从后往前累加找到恰好消耗 \(F1 - F2\) 饲料的那一天。

  2. 算法选择

    • 差分数组:将每头牛的进食区间 \([l, r]\) 转化为 \(a[l] \mathrel{+}= 1\)\(a[r+1] \mathrel{-}= 1\),实现 \(O(1)\) 区间标记
    • 前缀和还原\(b[i] = b[i-1] + a[i]\),得到每天实际在仓库吃饲料的牛数量
    • 逆向扫描:从第 \(D\) 天往前累加每天的消耗量 \(b[i]\),找到累计消耗等于 \(F1 - F2\) 的那天即为答案
  3. 关键步骤

    • 读取输入\(C, F1, F2, D\)\(C\) 条牛的区间 \([l_i, r_i]\)
    • 差分标记:对于每头牛,\(a[l_i] \mathrel{+}= 1\)\(a[r_i + 1] \mathrel{-}= 1\)
    • 前缀和还原\(b[i] = b[i-1] + a[i]\)\(i\)\(1\)\(D\)),\(b[i]\) 表示第 \(i\) 天吃饲料的牛数
    • 逆向扫描\(i\)\(D\) 递减到 \(1\)):
      • \(sum \mathrel{+}= b[i]\),累加从第 \(i\) 天到第 \(D\) 天的总消耗
      • \(sum = F1 - F2\):输出 \(i\) 并结束(要求最近日期,逆向首个满足条件的即为最大 \(i\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(C + D)\),差分标记 \(O(C)\),前缀和 + 逆向扫描 \(O(D)\)
    • 空间复杂度:\(O(D)\),差分数组和前缀和数组
  5. 差分数组与前缀和的核心思想

    • 区间操作降维:差分数组将 \(C\) 个区间加法转化为 \(2C\) 个单点更新,前缀和一次性还原每日实际值
    • 逆向贪心:从后往前扫描保证找到的是"最近"的运到日期(最大的满足条件的 \(i\)
    • 总量守恒\(F1 - F2\) 即为从运到日到第 \(D\) 天的总消耗量,逆向累加找到恰好匹配点
    • 适用于"区间覆盖统计 + 逆向查找匹配点"类问题,核心是差分高效处理区间和逆向扫描定位答案

【解题思路】

【算法标签】

普及 #差分

【代码详解】

#include <bits/stdc++.h>
using namespace std;

// 定义常量
const int N = 2005;  // 最大天数范围

// 变量定义
int c;      // 喂养记录的条数
int f1;     // 第一天之前FJ喂养的数量
int f2;     // 第d天之后FJ喂养的数量
int d;      // 总天数
int a[N];   // 差分数组
int b[N];   // 前缀和数组,表示每天Bessie喂养的次数

int main()
{
    // 输入数据
    cin >> c >> f1 >> f2 >> d;

    // 处理每条喂养记录
    for (int i = 1; i <= c; i++)
    {
        int l, r;  // 每条记录的起始天和结束天
        cin >> l >> r;

        // 使用差分数组
        // 在l天开始增加一次喂养
        a[l]++;
        // 在r+1天减少一次喂养
        a[r + 1]--;
    }

    // 计算前缀和,得到每天Bessie喂养的次数
    for (int i = 1; i <= d; i++)
    {
        b[i] = b[i - 1] + a[i];
    }

    // 从第d天往前计算
    int sum = 0;  // 从第i天到第d天的总喂养次数
    for (int i = d; i >= 1; i--)
    {
        sum += b[i];  // 累加第i天的喂养次数

        // 检查条件:f1 - sum = f2
        // 即:sum = f1 - f2
        if (sum == f1 - f2)
        {
            cout << i << endl;  // 输出结果
            break;
        }
    }

    return 0;
}

【运行结果】

3 14 4 10
1 9
5 8
8 12
6
posted @ 2026-08-27 11:07  团爸讲算法  阅读(4)  评论(0)    收藏  举报