給定 N 個整數(|值| ≤ 10,000),從中選出 K 個數,使其乘積最大。若乘積相同,則選擇總和最大的方案。輸出該子序列的元素和。
sum1 = sum - removed + added 並比較 removed1 * added2 vs removed2 * added1 等價於比較乘積比值。abs(a) != abs(b) ? abs(a) > abs(b) : a > b。#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int n, k, a[10005];
bool cmpAbs(int x, int y) {
if (abs(x) != abs(y)) return abs(x) > abs(y);
return x > y; // 同絕對值,正數優先
}
int solve() {
sort(a, a + n, cmpAbs);
int sum = 0, negCnt = 0;
for (int i = 0; i < k; ++i) {
sum += a[i];
if (a[i] < 0) ++negCnt;
}
if (negCnt % 2 == 0) {
// 已為正乘積,但需檢查是否含 0 導致乘積為 0
if (a[k-1] == 0) {
sort(a, a + n);
sum = 0;
for (int i = n-1; i >= n-k; --i) sum += a[i];
}
return sum;
}
// 負數為奇數,嘗試兩種替換
int sumA = -INF, sumB = -INF;
int remA = 0, addA = 0, remB = 0, addB = 0;
// 方案 A:換掉一個正數,換入一個負數
for (int i = k-1; i >= 0; --i) if (a[i] > 0) {
for (int j = k; j < n; ++j) if (a[j] < 0) {
sumA = sum - a[i] + a[j];
remA = abs(a[i]); addA = abs(a[j]);
break;
}
if (sumA != -INF) break;
}
// 方案 B:換掉一個負數,換入一個非負數
for (int i = k-1; i >= 0; --i) if (a[i] < 0) {
for (int j = k; j < n; ++j) if (a[j] >= 0) {
sumB = sum - a[i] + a[j];
remB = abs(a[i]); addB = abs(a[j]);
break;
}
if (sumB != -INF) break;
}
// 處理無法替換或替換涉及 0 的情況
if (addB == 0 && sumA == -INF) { // 只能換入 0 且無方案 A
sort(a, a + n);
sum = 0;
bool hasZero = false;
for (int i = n-1; i > n-k; --i) {
if (a[i] == 0) hasZero = true;
sum += a[i];
}
if (hasZero) sum += a[n-k];
return sum;
}
if (sumA == -INF && sumB == -INF) { // 兩種方案都不可行
sort(a, a + n);
sum = 0;
for (int i = n-1; i >= n-k; --i) sum += a[i];
return sum;
}
if (sumA == -INF) return sumB;
if (sumB == -INF) return sumA;
// 比較乘積:addA/remA vs addB/remB <=> addA*remB vs addB*remA
long long prodCmp = 1LL * addA * remB - 1LL * addB * remA;
if (prodCmp == 0) return max(sumA, sumB);
return (prodCmp > 0) ? sumA : sumB;
}
int main() {
while (cin >> n >> k, n || k) {
for (int i = 0; i < n; ++i) cin >> a[i];
cout << solve() << '\n';
}
return 0;
}
本題是貪心策略與細節處理的經典結合。核心在於「按絕對值排序優先取大」,並通過有限的替換操作將負數個數調整為偶數。關鍵難點在於乘積溢出的避免(用分數比較)以及 0 邊界情況的完備處理。