提供一个效率优秀的贪心做法。
首先排列只需要考虑奇偶性,设未确定的数中有 a 个奇数 b 个偶数。
对于两个中间全是 0 的已经确定的数,考虑怎样安排中间的 0 能使答案尽量小。
设这两个数模 2 为 x, y ,中间有 k 个数未确定。
分类讨论。
如果 x != y ,无论中间放什么数,都可以调整位置(只要和 x 奇偶性相同的在 x 一侧,其他在另一侧)使得对答案贡献恰好为 1 。
如果 x = y ,那么中间如果能全部放与 x, y 奇偶性相同的数就不会增加答案,
但这样会付出一定代价,代价就是必须用 k 个 a (或 b )。
而如果中间不全部放与 x, y 奇偶性相同的数的话,无论放什么数,
都可以调整位置(只要奇数在一块偶数在一块即可)使得对答案恰好贡献 2 。
那么不难发现需要决策的部分只有 x = y 中间全放一样的数这种情况,
而且有代价和收益,收益都是 2 ,代价不等,对于两种奇偶性能承受的代价分别为 a, b 。
将这些决策按代价排序,尽量选择代价小的去填,即可得到最大收益,即最小答案。
边界问题在于一些前缀 0 和一些后缀 0 难以判断,
直接暴力枚举钦定 p[0] 和 p[n + 1] 的奇偶性即可。
复杂度 $O(n)$ ,排序部分用基数排序。
然而这题 n 只有 100 。。。表示不满。
#include <cstdio>
#include <algorithm>
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long ll;
struct {
inline operator int () { int x; return scanf("%d", &x), x; }
inline operator ll () { ll x; return scanf("%lld", &x), x; }
template<class T> inline void operator () (T &x) { x = *this; }
template<class T, class ...A> inline void operator () (T &x, A &...a)
{ x = *this; this -> operator ()(a...); }
} read;
const int maxn = 105;
int p[maxn], tot[2];
int q[2][maxn];
int solve(int n) {
int las = 0;
int res = 0;
for(int i = 1; i <= n + 1; i ++)
if(p[i]) {
if(p[i] == p[las])
++ q[p[i] - 2][i - las - 1];
else
++ res;
las = i;
}
for(int i = 0; i < 2; i ++) {
int max = tot[i];
for(int x = 0; x <= max; x ++) {
while(q[i][x] and x <= max) {
max -= x;
-- q[i][x];
}
}
}
for(int i = 0; i <= n; i ++) {
res += 2 * (q[0][i] + q[1][i]);
q[0][i] = q[1][i] = 0;
}
return res;
}
int main() {
int n = read;
if(n == 1) return puts("0"), 0;
tot[0] = n >> 1;
tot[1] = (n + 1) >> 1;
for(int i = 1; i <= n; i ++) {
int x = read;
if(x) {
p[i] = 2 + (x & 1);
-- tot[p[i] - 2];
}
}
int ans = 1000000000;
for(int i = 2; i <= 3; i ++)
for(int j = 2; j <= 3; j ++) {
p[0] = i;
p[n + 1] = j;
ans = std::min(ans, solve(n));
}
printf("%d\n", ans);
}