算法-疯狂队列
前言
起初是因为上周参加一个算法竞赛时有一道题如下:
第一行输入一个数T,表示测试案例数量,第二行输入一个数n,第三行输出一个结果数output。
这个结果数要求从1-n开始,找到任意一种排列方式最大化数组两两之差的和。你现在可能听起来很懵,接下来我将举个例子来说明:
比如:
输入:
- 第一行:2(表示两个测试案列)
- 第二行:5(表示从1-5)
输出:
- 第三行:11(为什么是11呢,因为当数组为1-5,排列如3-2-5-1-4这样两两之差和为(3-2)+(5-2)+(5-1)+(4-1)=1+3+4+3=11为这5个数排序的最大值)
输入: - 第四行:1(第二个测试案例,只有一个数1)
输出:
- 第五行:1
然后当时想了挺久的吧,以为是找什么关系等差数列什么的,后面就上网搜到了一篇类似的OJ,链接如下:牛客
接下来我就以这篇OJ为例来讲解
一、题目介绍
这道题就跟我刚才上面介绍的那道题差不多,我们可以先把这个数组从小到大排序好并且每个都编好编号,但是因为奇数个数不能整除2,按照计算机里面的表示,如5/2=2。因此我们把最中间那个数也作为高数,所以高数会总比低数多一个。如输入
5 10 25 40 25按从小到大排序后为
5
⏟
低
1
10
⏟
低
2
25
⏟
高
1
25
⏟
高
2
40
⏟
高
3
\underbrace{5}_{低1} \underbrace{10}_{低2} \underbrace{25}_{高1} \underbrace{25}_{高2} \underbrace{40}_{高3}
低1
5低2
10高1
25高2
25高3
40
虽然这道题的数组个数为奇数,但是输入数组个数也可能为偶数,所以我们要分情况讨论:
第一种情况:当数组个数为奇数时(如题目一样):
最大化排序方式为25-10-40-5-25,我们用上面的编号来表示
25
⏟
高
1
10
⏟
低
2
40
⏟
高
3
5
⏟
低
1
25
⏟
高
2
\underbrace{25}_{高1} \underbrace{10}_{低2} \underbrace{40}_{高3} \underbrace{5}_{低1} \underbrace{25}_{高2}
高1
25低2
10高3
40低1
5高2
25注意这里最左边的是高1,这里恰好高1和高2相等了,如果换成其他的数,这里要放的是高1。我们可以观察到这个形式其实就是高-低-高-低-高的形式。然后两两相减为
25
−
10
⏟
高
1
−
低
2
+
40
−
10
⏟
高
3
−
低
2
+
40
−
5
⏟
高
3
−
低
1
+
25
−
5
⏟
高
2
−
低
1
\underbrace{25-10}_{高1-低2} +\underbrace{40-10}_{高3-低2}+ \underbrace{40-5}_{高3-低1} +\underbrace{25-5}_{高2-低1}
高1−低2
25−10+高3−低2
40−10+高3−低1
40−5+高2−低1
25−5然后我们加起来,把高的放一起,低的放一起,就变为了下面式子:
高
1
+
高
2
+
高
3
+
高
3
−
2
×
(
低
1
+
低
2
)
高1+高2+高3+高3-2\times(低1+低2)
高1+高2+高3+高3−2×(低1+低2),这个式子让我们想到可以转换成下面形式:
2
×
(
高
1
+
高
2
+
高
3
)
−
2
×
(
低
1
+
低
2
)
−
高
1
−
高
2
2\times(高1+高2+高3)-2\times(低1+低2)-高1-高2
2×(高1+高2+高3)−2×(低1+低2)−高1−高2即可。因为我们发现高1和高2最大化数组排列时,分别位居数组两侧,所以计算值时只会被计算一次,其实就是最小的高数和第二小的高数。而高3最大在中间会被计算两次,其余的低数也都是在中间,也会被计算两次。所以我们想到把全部高数和低数各自分开相加后,因为高1和高2被多算了一次,要减去,所以就是高数和减去低数和然后乘以2再减去高1和高2就是最后的答案。
第二种情况:当数组个数为偶数时,可以整除2,所以高数低数各占一半
我把上面的题目进行修改,改为4个数:5 10 25 40
先进行排序编号:
5
⏟
低
1
10
⏟
低
2
25
⏟
高
1
40
⏟
高
2
\underbrace{5}_{低1} \underbrace{10}_{低2} \underbrace{25}_{高1} \underbrace{40}_{高2}
低1
5低2
10高1
25高2
40
然后最大化排序方式为:
10
⏟
低
2
40
⏟
高
2
5
⏟
低
1
25
⏟
高
1
\underbrace{10}_{低2} \underbrace{40}_{高2} \underbrace{5}_{低1} \underbrace{25}_{高1}
低2
10高2
40低1
5高1
25这个式子其实就变成了一个低-高-低-高的形式。两两相减得:
40
−
10
⏟
高
2
−
低
2
+
40
−
5
⏟
高
2
−
低
1
+
25
−
5
⏟
高
1
−
低
1
\underbrace{40-10}_{高2-低2}+ \underbrace{40-5}_{高2-低1} +\underbrace{25-5}_{高1-低1}
高2−低2
40−10+高2−低1
40−5+高1−低1
25−5还是高的放一起低的放一起:
高
1
+
高
2
+
高
2
−
低
1
−
低
1
−
低
2
高1+高2+高2-低1-低1-低2
高1+高2+高2−低1−低1−低2
转换成:
2
×
(
高
1
+
高
2
)
−
2
×
(
低
1
+
低
2
)
−
高
1
+
低
2
2\times(高1+高2)-2\times(低1+低2)-高1+低2
2×(高1+高2)−2×(低1+低2)−高1+低2这里要注意,奇数时我们是多加了一个要减去,偶数时这里我们是多减了一个要加回来。因为个数为偶数所以最大的低数会放在最左边,最小的高数会放在最右边,这两个数都会只被计算一次。其余数都会被计算两次。而且注意最大低数因为是减去了两次,所以要加回来一个。
二、代码实现
#include<iostream>
#include<algorithm>
using namespace std;
int cal(int* num, int length) { //输入数组和长度
//先升序排序
sort(num, num + length);
int bigsum = 0;
int smallsum = 0;
int ans = 0; //结果
//取中位数
int mid = length / 2;
for (int i = 0; i < mid; i++) {
smallsum += num[i]; //低数累加
}
for (int i = mid; i < length; i++) {
bigsum += num[i]; //高数累加
}
//判断是奇数还是偶数
if (length & 1) { //如果是奇数
return 2 * (bigsum - smallsum) - num[mid] - num[mid + 1];
} else {
return 2 * (bigsum - smallsum) - num[mid] +num[mid - 1];
}
}
int main() {
int n;
cin >> n;
int*num=new int[n];
for (int i = 0; i < n; i++) {
cin >> num[i];
}
cout << cal(num, n);
delete[] num;
return 0;
}
结果也是成功AC:
三、写在最后
因为这是本人第一次写博客,如果有什么错误或者写的不好的地方,欢迎各位提出宝贵的意见。
最后就是写一段勉励的话吧:
感觉上了大学后,你会感觉很多人比你优秀或者怎么怎么样。每个人对优秀有自己的标准,你不能达到所有人认定的优秀条件,可那有怎么样,你可以大声坦然自己的平庸,也可以很坚定的说出其实我也不错。人民日报说过:“无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁,焦虑回调你本就不多的热情和定力。”如果说,小学初中高中你的坦途顺利,那么我祝你成年迈进大学依旧成功,如果不能,那我希望你可以悦纳自己,接受自己的一切,包括你的暗淡无光与无人在意。
“抬头自卑,低头自得,唯有平视,才能看见真实的自己。”
愿你我都能摆脱内耗,接受平庸!