输入样例1:
5 3
-100000
-10000
2
100000
10000
输出样例1:
999100009
输入样例2:
5 3
-100000
-100000
-2
-100000
-100000
输出样例2:
-999999829
分析:这是一个贪心题,需要分情况进行讨论,我们需要单独存储正数和负数(0按正数处理),第一种情况,给定的数的个数与要选的数的个数相同,那么这个直接全选就行了,还有一种情况就是全是负数,这个也要单独分情况进行讨论,我们先对负数进行排序,如果要是要选的数的个数为奇数,那么最终结果一定是负数,那么我们就从最大的负数开始选,选k个数为止,因为负数越大则其绝对值越小,如果要是要选的数的个数为偶数,因为最后结果是一个正值,那么我们就从最小的负数开始选,选完为止。
下面我们着重看一下存在正数的时候应该怎么选,首先如果不是把所有的数全选(即至少可以空出一个数不选)且存在正数,那么最后结果一定可以是个非负数(充其量我们不选一个正数或者不选一个负数,这个根据我们的乘积正负性来决定),这个时候如果所选的数的个数k是一个奇数,那我们最终选的数中至少存在一个正数,那么为了使乘积最大,我们就先选上正数中最大的那个数,如果k是偶数则不用做这步处理,剩下我们就只需要选择偶数个数了,我们依次比较绝对值最大的两个负数的乘积和两个正数的乘积,(需要注意的是我们一定要成对成对地选),谁大选谁,直到选完为止,这样就是最优解了,具体代码写的比较复杂,下面是代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e5+10,mod=1000000009;
typedef long long ll;
ll z[N],f[N];
int main()
{
int n,k;
int zcnt=0,fcnt=0;
cin>>n>>k;
ll t;
for(int i=1;i<=n;i++)
{
scanf("%lld",&t);
if(t>=0) z[++zcnt]=t;//包含0
else f[++fcnt]=t;
}
sort(z+1,z+zcnt+1);
sort(f+1,f+fcnt+1);
ll ans=1;
if(fcnt+zcnt==k)
{
for(int i=1;i<=fcnt;i++)
ans=ans*z[i]%mod;
for(int i=1;i<=zcnt;i++)
ans=ans*z[i]%mod;
if(fcnt&1&&ans) printf("-");
printf("%lld",(ans+mod)%mod);
return 0;
}
if(zcnt==0)//只存在负数
{
if(k&1)//所选数的个数为奇数,那么就从负数中较大的开始选
{
printf("-");
for(int i=fcnt;i>fcnt-k;i--)
ans=ans*(-f[i])%mod;
}
else//所选数的个数为偶数,那么就从负数中较小的开始选
{
for(int i=1;i<=k;i++)
ans=ans*f[i]%mod;
}
}
else//存在正数
{
if(k&1)//所选数的个数为奇数,则先把正数中值最大的数选出来,再进行比较选取其他数
ans=ans*z[zcnt--]%mod,k--;
int i,j;
for(i=1,j=zcnt;i<=fcnt&&j>0&&k;)
{
if(f[i]*f[i+1]>z[j]*z[j-1])//选两个当前可选的最小负数
{
ans=f[i]*f[i+1]%mod*ans%mod;
i+=2;
k-=2;
}
else//选两个当前可选的最大正数
{
ans=z[j]*z[j-1]%mod*ans%mod;
j-=2;
k-=2;
}
}
while(i<=fcnt&&k)
{
ans=f[i]*f[i+1]%mod*ans%mod;
i+=2;
k-=2;
}
while(j>0&&k)
{
ans=z[j]*z[j-1]%mod*ans%mod;
j-=2;
k-=2;
}
}
printf("%lld",(ans+mod)%mod);
return 0;
}