top1编程
← 返回题目
题解

绿化方案

1 条题解

  • 0
    @ 2026-8-6 1:17:21

    P4788 绿化方案(提高)

    解题思路

    第一步,读懂题目。 社区有 n 条路段,每条路段最多种 1 棵树。居民给出 h 条建议,每条建议说路段 b 到 e 之间至少要种 t 棵树,区间可以交叉。问满足所有建议至少要种多少棵树。

    第二步,确定贪心策略。 把建议按右端点 e 从小到大排序。处理每条建议时,先数一数它要求的区间 [b,e] 里已经种了多少棵树,如果不够 t 棵,就在这个区间里补种,补到够为止。

    第三步,补种时从右往左种。 补种的树尽量种在区间的右端,也就是从 e 开始往左找空位。为什么?因为还没处理的建议,右端点都更大,种在右边的树最容易同时满足后面区间更靠右的建议,一棵树能多帮几条建议,这样种的总数最少。

    第四步,为什么按 e 排序? 右端点小的区间先处理,它的树种在最右边,就能被后面右端点更大的区间“借用”。如果先处理右端点大的区间,树种得靠左,后面的小区间用不上,就会多种树。

    第五步,处理边界。 一个位置最多只能种一棵树,补种时遇到已经种过的位置要跳过,继续往左找空位。建议的区间可以交叉甚至互相包含,都要按顺序处理。

    第六步,举一个例子。 n=9,建议 (1,4,2)、(3,5,2)、(4,6,2)、(8,9,2)。按 e 排序后先处理 (1,4,2),在位置 4 和 3 种树;再处理 (3,5,2),区间里已有 3、4 两棵,够了;再处理 (4,6,2),已有 4 一棵,在位置 6 补一棵;最后处理 (8,9,2),在位置 9 和 8 种两棵。一共 5 棵。

    参考代码

    // 绿化方案:把建议按右端点排序,每个建议先数区间里已种的树,不够就从右往左补种,种树总数最少
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    struct Advice {
        int b;
        int e;
        int t;
    };
    
    Advice advices[100005];
    
    // 比较函数:右端点小的建议排在前面
    bool compareEnd(const Advice &a, const Advice &b) {
        return a.e < b.e;
    }
    
    bool planted[100005];
    
    int main() {
        int n, h;
        scanf("%d%d", &n, &h);
        for (int i = 0; i < h; i++) {
            scanf("%d%d%d", &advices[i].b, &advices[i].e, &advices[i].t);
        }
        sort(advices, advices + h, compareEnd);
        int total = 0;
        for (int i = 0; i < h; i++) {
            // 统计这个建议的区间里已经种了多少棵树
            int count = 0;
            for (int pos = advices[i].b; pos <= advices[i].e; pos++) {
                if (planted[pos]) count++;
            }
            // 如果还不够 t 棵,就从区间右端开始往左找空位补种
            for (int pos = advices[i].e; pos >= advices[i].b && count < advices[i].t; pos--) {
                if (!planted[pos]) {
                    planted[pos] = true;
                    count++;
                    total++;
                }
            }
        }
        printf("%d\n", total);
        return 0;
    }
    

    复杂度分析

    排序需要 O(h log h)。处理每条建议时,最坏要扫描整个区间统计已种树的数量,所以最坏情况总时间复杂度是 O(h 乘 n)。空间 O(n),用一个布尔数组记录每个位置是否已经种树。

    • 1