P1873 [COCI 2011/2012 #5] EKO / 砍树

记录114

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
long long a[N];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	//sort(a+1,a+n+1);
	int L=1,R=4e5,mid=0;//二分答案,mid当作可能的结果带入 
	long long sum=0,ans=0;//sum注意数据范围 
	while(L<=R){
		sum=0;
		mid=(L+R)/2;
		for(int i=1;i<=n;i++){//存每棵树的多余高度 
			if(a[i]-mid>0) sum+=a[i]-mid;
		}
		if(sum>=m){//长度至少m,总和>=m 
			ans=mid;//存符合条件的值,然后更新 
			L=mid+1;
		}  
		else R=mid-1;
	}
	cout<<ans;//输出 
	return 0;//结束程序 
}

前言

我是一名专注信奥赛(CSP-J/S、NOIP)的教练。

  • 如果你觉得这篇题解对你有帮助,欢迎点击关注我的CSDN账号,我会持续更新高质量算法解析。
  • 我深知算法思维的构建远比单纯通过题目更重要,本系列题解不局限于AC代码的堆砌,而是致力于拆解题目背后的逻辑链条与核心知识点
  • 备赛路上若遇瓶颈,欢迎随时评论或私信,我将甄选典型疑难问题,通过视频讲解或撰写专项文章的形式,为你提供深度答疑。

题目传送门https://www.luogu.com.cn/problem/P1873


突破口

伐木工人 Mirko 需要砍 M 米长的木材。对 Mirko 来说这是很简单的工作,因为他有一个漂亮的新伐木机,可以如野火一般砍伐森林。不过,Mirko 只被允许砍伐一排树。

Mirko 的伐木机工作流程如下:Mirko 设置一个高度参数 H(米),伐木机升起一个巨大的锯片到高度 H,并锯掉所有树比 H 高的部分(当然,树木不高于 H 米的部分保持不变)。Mirko 就得到树木被锯下的部分。例如,如果一排树的高度分别为 20,15,10 和 17,Mirko 把锯片升到 15 米的高度,切割后树木剩下的高度将是 15,15,10 和 15,而 Mirko 将从第 1 棵树得到 5 米,从第 4 棵树得到 2 米,共得到 7 米木材。

Mirko 非常关注生态保护,所以他不会砍掉过多的木材。这也是他尽可能高地设定伐木机锯片的原因。请帮助 Mirko 找到伐木机锯片的最大的整数高度 H,使得他能得到的木材至少为 M 米。换句话说,如果再升高 1 米,他将得不到 M 米木材。


🔍 一、题目本质与目标

🎯 问题重述

  • N 棵树,高度为 a[1..N]
  • 设置一个锯片高度 H
  • 对每棵树:若 a[i] > H,则获得 a[i] - H 米木材;否则获得 0
  • 目标:找到最大的整数 H ,使得 总木材 ≥ M

✅ 关键词:

  • "最大 H" → 最优化目标
  • "至少 M 米" → 约束条件
  • "再升高 1 米就不够 " → H 是满足条件的上界

📌 样例解析(输入 #1)

cpp 复制代码
树高:20, 15, 10, 17
M = 7
  • H = 15 → 得到 (20-15)+(17-15) = 5+2 = 7 ✅
  • H = 16 → (20-16)+(17-16) = 4+1 = 5 < 7 ❌
  • 所以最大 H = 15

为什么能用二分?

观察 H 与 总木材量 sum(H) 的关系:

  • H 越大 → 锯得越少 → sum(H) 单调不增
  • H 越小 → 锯得越多 → sum(H) 单调不减

💡 单调性是二分搜索的前提!

我们要找:最大的 H,使得 sum(H) ≥ M

这正是典型的 "二分答案" 问题。

二分策略

  • 搜索空间 :H ∈ [0, max_height]
    (题目中树高 ≤ 4e5,但代码用了 R=1e9,更保险)
  • 判断函数 :给定 H,计算 sum = Σ max(0, a[i] - H)
  • 调整方向
    • sum ≥ M → H 可能还能更高 → L = mid + 1
    • 否则 → H 太高了 → R = mid - 1

✅ 最终答案记录在 ans 中(每次满足条件时更新)


代码分析

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
long long a[N]; // 存储树高,注意:虽然树高 ≤4e5,但用 long long 无妨
cpp 复制代码
int main(){
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) 
        cin >> a[i];
  • 读入数据,1-based 存储
cpp 复制代码
    int L = 1, R = 4e5, mid = 0; // 二分边界
    long long sum = 0, ans = 0;   // sum 必须 long long!

⚠️ 关键细节:

  • R = 4e5:虽然树高 ≤ 4e5,但设大一点无妨(安全)
  • sum 类型为 long long:因为 M 可达 2e9,且每棵树最多贡献 4e5,N=1e6 → 最大 sum ≈ 4e11,必须用 long long!
  • ans = 0:初始化答案
cpp 复制代码
    while(L <= R){
        sum = 0;
        mid = (L + R) / 2; // 当前尝试的高度 H = mid
cpp 复制代码
        for(int i = 1; i <= n; i++){
            if(a[i] - mid > 0) 
                sum += a[i] - mid;
        }
  • 计算在高度 mid 下能获得的总木材量
  • 等价于:sum += max(0LL, a[i] - mid);
cpp 复制代码
        if(sum >= m){ // 满足"至少 M 米"
            ans = mid;   // 记录当前可行解
            L = mid + 1; // 尝试更高的 H(更优解)
        }  
        else 
            R = mid - 1; // H 太高,降低
    }

🔥 这是二分答案的核心逻辑!

  • 我们要最大化 H ,所以当 sum ≥ M 时,不 return,而是继续尝试更大的 H
  • 最终 ans 保存的是最后一个满足条件的 H
cpp 复制代码
    cout << ans;
    return 0;
}

⚠️ 四、关键问题与优化建议

问题 说明
二分下界 L = 1 不够严谨,应设 L = 0(H 可为 0) 但题目保证"总和 > M",且树高 ≥1,所以 H=0 总能满足,不影响答案
mid 溢出 (L+R)/2 在 L,R ≤1e9 时安全(和 ≤2e9 < 2^31)
时间复杂度 O(log(maxH) × N) ≈ log₂(1e9) × 1e6 ≈ 30 × 1e6 = 3e7,C++ 可接受

与其他方法对比

方法 可行性 缺点
暴力枚举 H ❌ 不可行(H 可达 4e5,N=1e6 → 4e11 操作)
二分答案 + 暴力求和 ✅ 本题解法,O(N log maxH)
二分 + 前缀和优化 ✅ 可进一步优化到 O(N log N + log maxH × log N) (排序后用 upper_bound 找分界点,再用前缀和快速计算)

💡 前缀和优化思路(供拓展):

  1. 排序数组
  2. 预处理前缀和 pre[i] = a[1]+...+a[i]
  3. 对给定 H,用 upper_bound 找第一个 > H 的位置 pos
  4. sum = (pre[n] - pre[pos-1]) - H × (n - pos + 1)
  5. 这样每次 check 为 O(log N),总复杂度 O(N log N)

但本题 N=1e6,O(30×1e6)=3e7 已足够,无需复杂优化。

相关推荐
啊哦呃咦唔鱼1 小时前
leetcodehot100-347. 前 K 个高频元素
数据结构·算法·leetcode
玛丽莲茼蒿1 小时前
Leetcode hot100 多数元素【简单】
算法·leetcode·职场和发展
AbandonForce1 小时前
Map类:pair键值对|map的基本操作|operator[]
开发语言·c++·算法·leetcode
澈2071 小时前
C++核心:封装与static静态成员实战指南
开发语言·c++·算法
田梓燊1 小时前
力扣:146.LRU 缓存
算法·leetcode·缓存
_深海凉_1 小时前
LeetCode热题100-杨辉三角
算法·leetcode·职场和发展
ShineWinsu1 小时前
对于Linux:进程间通信IPC(匿名管道)的解析
linux·c++·面试·进程·通信·管道·ipc
wuyoula1 小时前
全新多平台电商代付商城源码
开发语言·c++·ui·小程序·php源码
小O的算法实验室2 小时前
2025年SEVC,面向进化计算的学习注入式优化,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进