题意: 给定一个长度为
的序列
,
,从中选择
个数,使得答案最大,问最大答案是最少。
题解1:
模拟就完事了,考虑完所有情况,
是不可能的,大概需要考虑的就是
为奇数和偶数的情况吧。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
ll pos[N], gp;
ll neg[N], gn;
int zero;
int n, k;
bool cmp(int a, int b) {
return a > b;
}
int main()
{
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) {
ll x; scanf("%lld", &x);
if(x == 0) zero++;
else if(x > 0) pos[++gp] = x;
else neg[++gn] = x;
}
sort(pos + 1, pos + 1 + gp, cmp);
sort(neg + 1, neg + 1 + gn);
ll res = 1;
if(gp == n) {
for(int i = 1; i <= k; i++) res = res * pos[i] % mod;
} else if(gn == n) {
if(k & 1) for(int i = n, j = 0; i >= 1 && j < k; i--, j++) res = res * neg[i] % mod;
else for(int i = 1; i <= k; i++) res = res * neg[i] % mod;
} else if(zero == n) {
res = 0;
} else {
//正数和负数够了,不够k那么必然是0
if(gp + gn >= k) {
//如果k是奇数,那么存在正数的情况就是gp>=1, 然后剩余gn个负数和gp-1个正数可以凑出来k-1
if(k & 1) {
if(gp >= 1) {
//判断是否可以得到正数答案
if(gp + gn / 2 * 2 >= k) {
res = res * pos[1] % mod;
int i = 2, j = 1, cnt = k - 1;
while(i + 1 <= gp && j + 1 <= gn && cnt) {
if(pos[i] * pos[i + 1] >= neg[j] * neg[j + 1]) {
res = res * pos[i] % mod * pos[i + 1] % mod;
i += 2, cnt -= 2;
}
else {
res = res * neg[j] % mod * neg[j + 1] % mod;
j += 2, cnt -= 2;
}
}
while(cnt && i + 1 <= gp) {
res = res * pos[i] % mod * pos[i + 1] % mod;
i += 2, cnt -= 2;
}
while(cnt && j + 1 <= gn) {
res = res * neg[j] % mod * neg[j + 1] % mod;
j += 2, cnt -= 2;
}
}
//gp+gn>=k,gp+gn/2*2<k,那么必然是gn&1,所以gp+gn=k
else {
for(int i = 1; i <= gp; i++) res = res * pos[i] % mod;
for(int i = 1; i <= gn; i++) res = res * neg[i] % mod;
}
}
//无pos,那么就先将所有负数都算上,最后统一处理负数
else if(gp < 1){
int j = 0;
for(int i = gn; i >= 1 && j < k; i--, j++) res = res * neg[i] % mod;
if(j < k) res = 0;
}
}
//k为偶,和上述的k&1后将k-1的情况差不多
else {
int cnt = gp / 2 * 2 + gn / 2 * 2;
if(cnt >= k) {
cnt = k;
int i = 1, j = 1;
while(i + 1 <= gp && j + 1 <= gn && cnt) {
if(pos[i] * pos[i + 1] >= neg[j] * neg[j + 1]) {
res = res * pos[i] % mod * pos[i + 1] % mod;
i += 2, cnt -= 2;
}
else {
res = res * neg[j] % mod * neg[j + 1] % mod;
j += 2, cnt -= 2;
}
}
while(cnt && i + 1 <= gp) {
res = res * pos[i] % mod * pos[i + 1] % mod;
i += 2, cnt -= 2;
}
while(cnt && j + 1 <= gn) {
res = res * neg[j] % mod * neg[j + 1] % mod;
j += 2, cnt -= 2;
}
}
//由于k是偶数,所以若sur<k,sur+cnt>=k,必然是sur=2
else {
int sur = (gp & 1) + (gn & 1);
if(cnt + sur >= k) {
if(zero > 0) res = 0;
else {
for(int i = 1; i <= gn; i++) res = res * neg[i] % mod;
for(int i = 1; i <= gp; i++) res = res * pos[i] % mod;
}
}
else res = 0;
}
}
}
else res = 0;
}
//统一解决下,若答案<0但是zero>0,那么答案为0更大
if(res < 0 && zero > 0) res = 0;
else res = (res % mod + mod) % mod;
printf("%lld\n", res);
return 0;
}
题解2:参考了 的思路:传送门
我们的目标是尽可能让答案为非负数。所以考虑从序列 中选择负数的情况必然是选择了一正一负,此情况只有序列 中不存在 才会出现。
情况
:
,所有数都得选
情况
:
,若
是偶数,那么选绝对值更大的
个,否则选绝对值更小的
个
情况
:对
排序后,显然
。
考虑
为偶数,那么就从两边取
此时若取到负数,必然是
时,因为取负数时必然剩一奇一偶,而
为情况
所讨论的,故答案必然大于等于
。
考虑
为奇数,若
等于
,那么答案就是
,因为全取负数答案就是负数。
若
,那么也取
,此时
,可以保证答案不为负数,所以要尽可能选绝对值大的,但是选择了负数会直接将整个值变为负,故选择正数中的最大的即
。
证明如下:
取了
后还得取
个数,
是偶数,此时相当于情况
,只不过初始值为
,可选数数量为
,要选数数量为
。由于
的结论,故答案也必然是大于等于
的。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
ll q[N];
int n, k;
int main()
{
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%lld", &q[i]);
sort(q + 1, q + 1 + n);
ll res = 1;
if(n == k || q[n] < 0) {
//奇数就选绝对值尽量小,偶数就选绝对值尽量大
if(k & 1) for(int i = n - k + 1; i <= n; i++) res = res * q[i] % mod;
else for(int i = 1; i <= k; i++) res = res * q[i] % mod;
}
else {
int l = 1, r = n;
//目标是为了使得最后的答案尽可能为非负数
//考虑无论如何k为奇数时都选择q[n],此时q[n]>=0,
//若q[n]为0那么答案就是0,若q[n]>0,则选择后正数数量减1,然后继续k为偶数,从[1~r]中选择
if(k & 1) res = res * q[r] % mod, r--, k--;
//考虑整个序列中出现q[x]*q[x+1]为负数必然是不存在0的时候
//如果选择了乘积为负数必然是只剩两个元素了
while(k) {
if(q[l] * q[l + 1] > q[r] * q[r - 1]) res = res * q[l] % mod * q[l + 1] % mod, l += 2;
else res = res * q[r] % mod * q[r - 1] % mod, r -= 2;
k -= 2;
}
}
res = (res % mod + mod) % mod;
printf("%lld\n", res);
return 0;
}