思路
先观察一对相邻的数 \(x,y\):
如果 \(x,y\) 奇偶性相同,那么 \(\left\lfloor\frac{x+y}{2}\right\rfloor=\frac{x+y}{2}\)。
如果 \(x,y\) 奇偶性不同,那么 \(x+y\) 为奇数,有 \(\left\lfloor\frac{x+y}{2}\right\rfloor=\frac{x+y-1}{2}\)。
因此可以统一写成 \(\left\lfloor\frac{x+y}{2}\right\rfloor=\frac{x+y-[x,y\text{ 奇偶不同}]}{2}\)。
设最终排列为 \(p_1,p_2,\dots,p_n\),其中相邻奇偶性不同的对数为 \(cnt\)。
那么原式为 \(\frac{\sum_{i=1}^{n-1}(p_i+p_{i+1})-cnt}{2}\)。
注意每个中间位置都会出现两次,而首尾只出现一次,因此
\(\sum_{i=1}^{n-1}(p_i+p_{i+1})=2\sum_{i=1}^n p_i-p_1-p_n\)。
于是答案变为 \(\sum_{i=1}^n p_i-\frac{p_1+p_n+cnt}{2}\)。
数组总和是固定的,所以我们真正需要做的是:
最大化 \(p_1+p_n+cnt\)。
首尾怎么选
对于确定的首尾奇偶性,\(cnt\) 只与整个排列的奇偶顺序有关,与具体数值无关。
所以如果首尾都要求是奇数,那么显然应该选择最大的两个奇数作为首尾。
同理:
- 首尾都是奇数:选择最大的两个奇数;
- 首尾都是偶数:选择最大的两个偶数;
- 首尾一奇一偶:分别选择最大的奇数和最大的偶数。
因此只需要考虑这三种情况。
中间怎么排
确定首尾以后,我们希望让相邻位置尽可能多地出现一奇一偶,因为每出现一次奇偶不同,就可以让答案减少 \(\frac12\)。
所以中间的数应该尽可能交替放置:
奇 偶 奇 偶 奇 偶 ...
或者:
偶 奇 偶 奇 偶 奇 ...
具体数值大小并不影响奇偶不同的数量,因此剩余的奇数和偶数按从小到大的顺序取即可。
代码中分别讨论三种首尾情况。
情况一:首尾都是奇数
先把最大的奇数放在开头,把第二大的奇数留到最后。
剩余位置尽量按照:
偶 奇 偶 奇 ...
的顺序放置。
d.eb (b[b.size () - 1]);
while (id1 + 2 < b.size () || id2 < c.size ()) {
if (id2 < c.size ()) d.eb (c[id2 ++]);
if (id1 + 2 < b.size ()) d.eb (b[id1 ++]);
}
d.eb (b[b.size () - 2]);
情况二:首尾都是偶数
同理,把最大的两个偶数放在首尾,中间尽量按照:
奇 偶 奇 偶 ...
排列。
情况三:首尾一奇一偶
分别把最大的奇数和最大的偶数放在两端,中间继续尽量奇偶交替。
三种情况分别计算答案,最后取最小值即可。
如何想到的
看到下取整后,可以先考虑它什么时候会真正产生影响。
对于两个数 \(x,y\),只有当它们一奇一偶时,\(x+y\) 才是奇数,此时 \(\left\lfloor\frac{x+y}{2}\right\rfloor\) 会比直接除以 \(2\) 少 \(\frac12\)。
所以自然会想到,我们应该尽量让相邻的两个数奇偶性不同。
继续把整个式子展开以后,会发现除了这个“奇偶不同的次数”以外,所有中间元素都会被计算两次,只有首尾元素只计算一次。
因此问题就被拆成了两个部分:
- 首尾尽量放大的数;
- 中间尽量让奇偶交替。
而首尾又只有“奇奇、偶偶、奇偶”三种可能,于是分别枚举这三种情况即可。
代码
#include <bits/stdc++.h>
#define rep(i,j,k) for (int i = (j); i <= (k); i ++)
#define per(i,j,k) for (int i = (j); i >= (k); i --)
#define uint unsigned int
#define ll long long
#define ull unsigned long long
#define db double
#define ldb long double
#define mkp make_pair
#define eb emplace_back
#define e_ emplace
#define ls (p << 1)
#define rs (p << 1 | 1)
//#define int ll
using namespace std;
const int N = 2e5 + 5;
//const int MOD = 998244353;
//const int MOD = 1000000007;
const int INF = 0x3f3f3f3f;
const ll LINF = 4e18;
int T;
int n;
int a[N];
vector<int> b, c, d;
signed main () {
//freopen (".in", "r", stdin);
//freopen (".out", "w", stdout);
ios::sync_with_stdio (0);
cin.tie (0);
cin >> T;
while (T --) {
b.clear ();
c.clear ();
cin >> n;
rep (i, 1, n) {
cin >> a[i];
if (a[i] % 2) b.eb (a[i]);
else c.eb (a[i]);
}
int id1 = 0, id2 = 0;
ll mn = LINF;
sort (b.begin (), b.end ());
sort (c.begin (), c.end ());
if (b.size () >= 2) {
d.clear ();
d.eb (b[b.size () - 1]);
while (id1 + 2 < b.size () || id2 < c.size ()) {
if (id2 < c.size ()) d.eb (c[id2 ++]);
if (id1 + 2 < b.size ()) d.eb (b[id1 ++]);
}
d.eb (b[b.size () - 2]);
ll ans = 0;
rep (i, 1, d.size () - 1)
ans += (d[i] + d[i - 1]) / 2;
mn = min (mn, ans);
}
if (c.size () >= 2) {
d.clear ();
id1 = id2 = 0;
d.eb (c[c.size () - 1]);
while (id1 < b.size () || id2 + 2 < c.size ()) {
if (id1 < b.size ()) d.eb (b[id1 ++]);
if (id2 + 2 < c.size ()) d.eb (c[id2 ++]);
}
d.eb (c[c.size () - 2]);
ll ans = 0;
rep (i, 1, d.size () - 1)
ans += (d[i] + d[i - 1]) / 2;
mn = min (mn, ans);
}
if (c.size () && b.size ()) {
d.clear ();
id1 = id2 = 0;
d.eb (b[b.size () - 1]);
while (id1 + 1 < b.size () || id2 + 1 < c.size ()) {
if (id2 + 1 < c.size ()) d.eb (c[id2 ++]);
if (id1 + 1 < b.size ()) d.eb (b[id1 ++]);
}
d.eb (c[c.size () - 1]);
ll ans = 0;
rep (i, 1, d.size () - 1)
ans += (d[i] + d[i - 1]) / 2;
mn = min (mn, ans);
}
cout << mn << '\n';
}
return 0;
}
复杂度
每组数据需要分别对奇数和偶数排序,总时间复杂度为 \(O(N\log N)\)。
构造排列和计算答案都是 \(O(N)\)。
浙公网安备 33010602011771号