
洛谷【算法1-5】贪心 ——P4995 跳跳!
排序处理:将石头高度升序排列(a[1]到a[n]方便后续用双指针选择最高和最低的石头双指针策略:l指针初始指向最低石头(a[1]h指针初始指向最高石头(a[n]跳跃逻辑:第一跳特殊处理:从地面(0)跳到最高石头l++;h--;// 移动指针边界处理:当石头数量为奇数时,最后一个石头会自动被处理使用确保所有石头都被访问。
贪心入门——P4995 跳跳!
P4995 跳跳!
题目描述
你是一只小跳蛙,你特别擅长在各种地方跳来跳去。
这一天,你和朋友小 F 一起出去玩耍的时候,遇到了一堆高矮不同的石头,其中第 i i i 块的石头高度为 h i h_i hi,地面的高度是 h 0 = 0 h_0 = 0 h0=0。你估计着,从第 i i i 块石头跳到第 j j j 块石头上耗费的体力值为 ( h i − h j ) 2 (h_i - h_j) ^ 2 (hi−hj)2,从地面跳到第 i i i 块石头耗费的体力值是 ( h i ) 2 (h_i) ^ 2 (hi)2。
为了给小 F 展现你超级跳的本领,你决定跳到每个石头上各一次,并最终停在任意一块石头上,并且小跳蛙想耗费尽可能多的体力值。
当然,你只是一只小跳蛙,你只会跳,不知道怎么跳才能让本领更充分地展现。
不过你有救啦!小 F 给你递来了一个写着 AK 的电脑,你可以使用计算机程序帮你解决这个问题,万能的计算机会告诉你怎么跳。
那就请你——会写代码的小跳蛙——写下这个程序,为你 NOIp AK 踏出坚实的一步吧!
输入格式
输入一行一个正整数 n n n,表示石头个数。
输入第二行 n n n 个正整数,表示第 i i i 块石头的高度 h i h_i hi。
输出格式
输出一行一个正整数,表示你可以耗费的体力值的最大值。
输入输出样例 #1
输入 #1
2
2 1
输出 #1
5
输入输出样例 #2
输入 #2
3
6 3 5
输出 #2
49
说明/提示
样例解释
两个样例按照输入给定的顺序依次跳上去就可以得到最优方案之一。
数据范围
对于 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n,有 0 < h i ≤ 1 0 4 0 < h_i \leq 10 ^ 4 0<hi≤104,且保证 h i h_i hi 互不相同。
对于 10 % 10\% 10% 的数据, n ≤ 3 n \leq 3 n≤3;
对于 20 % 20\% 20% 的数据, n ≤ 10 n \leq 10 n≤10;
对于 50 % 50\% 50% 的数据, n ≤ 20 n \leq 20 n≤20;
对于 80 % 80\% 80% 的数据, n ≤ 50 n \leq 50 n≤50;
对于 100 % 100\% 100% 的数据, n ≤ 300 n \leq 300 n≤300。
题解分析
小跳蛙需要从地面(高度为0)出发,依次跳到每个石头各一次,最终停在任意一块石头上。每次跳跃的体力消耗为两石头高度差的平方。目标是找到一种跳跃顺序,使得总耗费体力值最大。
解题思路
贪心策略
要使总体力消耗最大,应最大化每次跳跃的高度差。因此:
- 将石头按高度排序
- 采用高低交替跳跃的策略:
- 从地面跳到最高的石头
- 从最高跳到最低
- 从最低跳到次高
- 依此类推…
正确性证明
- 平方函数是凸函数,高度差越大,增量越大
- 这种交替跳跃方式能保证每次跳跃都尽可能产生最大高度差
- 数学归纳法可证明这是全局最优解
代码实现分析
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int N = 310;
long long a[N], n;
int main() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + n + 1); // 升序排序
int l = 0, h = n; // l指向最低,h指向最高
long long ans = 0;
bool flag = true; // 标记跳跃方向
while(l < h){
if(flag){
// 跳到低的
ans +=pow(a[l++] - a[h], 2);
}else{
// 跳到高的
ans +=pow(a[l] - a[h--], 2);
}
flag = !flag;
}
cout << ans << endl;
return 0;
}
总结
-
排序处理:
-
将石头高度升序排列(
a[1]到a[n]
) -
方便后续用双指针选择最高和最低的石头
-
-
双指针策略:
*
l
指针初始指向最低石头(a[1]
)h
指针初始指向最高石头(a[n]
)
-
跳跃逻辑:
-
第一跳特殊处理:从地面(0)跳到最高石头
-
之后交替选择高低石头:
ans += pow(a[l] - a[h], 2); l++; h--; // 移动指针
-
-
边界处理:
-
当石头数量为奇数时,最后一个石头会自动被处理
-
使用
while(l <= h)
确保所有石头都被访问
-
更多推荐
所有评论(0)