⭐每日一题⭐专栏

written by SJTU-XHW

本人学识有限,解析难免有错,恳请读者能够批评指正,本人将不胜感激!


LuoGu P4995. 跳蛙

题目描述

你是一只小跳蛙,你特别擅长在各种地方跳来跳去。

这一天,你和朋友小 F 一起出去玩耍的时候,遇到了一堆高矮不同的石头,其中第 $i$ 块的石头高度为 $h_i$,地面的高度是 $h_0 = 0$。你估计着,从第 $i$ 块石头跳到第 $j$ 块石头上耗费的体力值为 $(h_i - h_j) ^ 2$,从地面跳到第 $i$ 块石头耗费的体力值是 $(h_i) ^ 2$。

为了给小 F 展现你超级跳的本领,你决定跳到每个石头上各一次,并最终停在任意一块石头上,并且小跳蛙想耗费尽可能多的体力值。

你可以使用计算机程序帮你解决这个问题。

说明

输入格式

输入一行一个正整数 $n$,表示石头个数。

输入第二行 $n$ 个正整数,表示第 $i$ 块石头的高度 $h_i$。

输出格式

输出一行一个正整数,表示你可以耗费的体力值的最大值。

样例 1 输入

1
2
2
2 1

样例 1 输出

1
5

样例 2 输入

1
2
3
6 3 5

样例 2 输出

1
49

数据范围

对于 $1 \leq i \leq n$,有 $0 < h_i \leq 10 ^ 4$,且保证 $h_i$ 互不相同。

对于 $10\%$ 的数据,$n \leq 3$;

对于 $20\%$ 的数据,$n \leq 10$;

对于 $50\%$ 的数据,$n \leq 20$;

对于 $80\%$ 的数据,$n \leq 50$;

对于 $100\%$ 的数据,$n \leq 300$。

思路

第一次读题差点看错,要求消耗尽可能多的体力……这题又没辙了,还是贪心法:每次选择前遍历查找所有数中,和当前位置高度差最大的,且没有选择过的数;如果存在多个相同的高度差,那么类似八皇后问题,进行递归(dfs),最终找到最大值;

再一想,没必要啊,如果我一开始就将数据排序的话,每次相当于 “横跳” 数组两端的数据即可保证始终是最大值!时间复杂度平均在 $O(n\cdot log\space n)$,而且空间复杂度 $O(log\space n)$,比上面的想法要好;

注:这题的贪心法本人仍然没有证明出来,转化为数学命题:

对于任意一串正整数序列:$a_1,a_2,…,a_n$,$a_1^2+(a_2-a_1)^2+(a_3-a_2)^2+\cdots+(a_n-a_{n-1})^2$ 取最大值 $\Longrightarrow$ 对任意的 $i\gt j$,$(a_i-a_{i-1})^2\ge(a_i-a_j)^2$,当 $j=i-1$ 取等;

有无会证的同学帮帮忙?

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <cstdio>
using namespace std;

int divide(int* arr, int left, int right) {
int pivot = arr[left];
while (left < right) {
while (arr[right] > pivot && left < right) --right;
if (left < right) arr[left] = arr[right];
while (arr[left] <= pivot && left < right) ++left;
if (left < right) arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}

void quickSort(int* arr, int left, int right) {
if (left >= right) return;
int mid = divide(arr, left, right);
quickSort(arr, left, mid - 1);
quickSort(arr, mid + 1, right);
}

int main() {
int n, left, right, ans; scanf("%d", &n);
int* height = new int[n] {0};
left = 0, right = n - 1;
for (int i = 0; i < n; ++i) scanf("%d", &(height[i]));
quickSort(height, 0, n - 1);
ans += height[right] * height[right]; // 跳至 right
while (left < right) {
ans += (height[right] - height[left]) * (height[right] - height[left]); --right; // 跳至 left,更新右端点
ans += (height[right] - height[left]) * (height[right] - height[left]); ++left; // 跳至 right,更新左端点
}
printf("%d", ans);
return 0;
}

不出意外的话,就要出意外了——只拿了50分,提交了 5 遍都有问题,整个人都不好了……直到我在社区看到这句话:

蚌埠住了😶 气得我把所有数据全换成 long long

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
using namespace std;

long long divide(long long* arr, long long left, long long right) {
long long pivot = arr[left];
while (left < right) {
while (arr[right] > pivot && left < right) --right;
if (left < right) arr[left] = arr[right];
while (arr[left] <= pivot && left < right) ++left;
if (left < right) arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}

void quickSort(long long* arr, long long left, long long right) {
if (left >= right) return;
long long mid = divide(arr, left, right);
quickSort(arr, left, mid - 1);
quickSort(arr, mid + 1, right);
}

int main() {
long long n, left, right; cin >> n;
long long ans;
long long* height = new long long[n] {0};
left = 0, right = n - 1;
for (int i = 0; i < n; ++i) cin >> height[i];
quickSort(height, 0, n - 1);
ans += height[right] * height[right];
while (left < right) {
ans += (height[right] - height[left]) * (height[right] - height[left]);
--right;
ans += (height[right] - height[left]) * (height[right] - height[left]);
++left;
}
cout << ans;

delete[] height;
return 0;
}


评论
昼夜切换阅读模式