Maximum Subsequence UVA - 10747 贪心

问题

给出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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值