问题
给出k个数,从中选择k个,使得他们的积最大,积最大时和最大。
分析
参考:https://blog.csdn.net/keshuai19940722/article/details/18194631
首先将所有的数字按照绝对值从大到小排序,绝对值相等正数排在负数前面,取前k个数字,然后分情况讨论
如果前k个数字中包括0,那么乘积一定是0,所以尽量选择最大的数字,让和最大
如果前k个数字中包括偶数个负数,就是最优解
如果前k个数字中包括奇数个负数,分成两部分讨论:
(1)前k个数字中没有正数,那么从后面找一个正数替换绝对值最小的负数,找不到正数就找0,找不到0就重新排序,选择从前往后的k个数字
(2)前k个数字中有正数,因为负数时奇数个,有正数有负数。如果剩下的数字中只有正数(或者0),就是没有负数,那么就替换掉k中的绝对值最小负数,如果剩下的数字中只有负数(没有正数),那么就替换掉其中的一个正数。正数负数都有就比较哪种方法最优。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int maxn=10000+5;
int n,k,a[maxn],b[maxn];
bool cmp(int a,int b){
int aa=abs(a),ab=abs(b);
if(aa!=ab) return aa>ab;
else return a>b;
}
int cal(int t){
int ans=0;
sort(a,a+n,greater<int>());
for(int i=0;i<t;++i) ans+=a[i];
return ans;
};
int main(void){
// freopen("../Ch01_ex/UVA10747_in.txt","r",stdin);
// freopen("../Ch01_ex/UVA10747_out.txt","w",stdout);
while(scanf("%d%d",&n,&k)==2 && n){
for(int i=0;i<n;++i) scanf("%d",&a[i]);
sort(a,a+n,cmp);
int ans=0,flag=1,neg=0,lp=0,ln=0;
for(int i=0;i<k;++i){
if(a[i]==0){
flag=0;
break;
}else if(a[i]<0) ++neg;
if(a[i]>0) lp=i;
else if(a[i]<0) ln=i;
}
if(!flag){
ans=cal(k);
}else if(neg%2==0 || k==n){
for(int i=0;i<k;++i) ans+=a[i];
}else {
//负,0,正
int n1=0,n2=0,n3=0;
for(int i=k;i<n;++i){
if(n1==0 && a[i]<0) n1=i;
else if(a[i]==0) ++n2;
else if(n3==0 && a[i]>0) n3=i;
}
if(neg==k) {
if(n3==0){
//只有0或者负
ans=cal(k);
}else {
swap(a[ln],a[n3]);
for(int i=0;i<k;++i) ans+=a[i];
}
}else{
//全是0
if(n3==0 && n1==0) {
ans=cal(k);
}else if(n1==0){
//没有负
swap(a[ln],a[n3]);
for(int i=0;i<k;++i){
ans+=a[i];
}
}else if(n3==0){
//只有负
swap(a[lp],a[n1]);
for(int i=0;i<k;++i){
ans+=a[i];
}
}else{
//有正有负
if(a[n3]*a[lp]>a[n1]*a[ln] || (a[n3]*a[lp]==a[n1]*a[ln]
&& a[n3]-a[ln]>a[n1]-a[lp])){
swap(a[ln],a[n3]);
for(int i=0;i<k;++i){
ans+=a[i];
}
}else {
swap(a[lp],a[n1]);
for(int i=0;i<k;++i){
ans+=a[i];
}
}
}
}
}
printf("%d\n",ans);
}
return 0;
}