蓝桥杯2018省赛-乘积最大-java

题目

给定N个整数A1, A2, ... AN。请你从中选出K个数,使其乘积最大。
请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以1000000009的余数。
注意,如果X<0, 我们定义X除以1000000009的余数是负(-X)除以1000000009的余数。
即:0-((0-x) % 1000000009)

输入格式第一行包含两个整数N和K。以下N行每行一个整数Ai。

  对于40%的数据,1 <= K <= N <= 100
  对于60%的数据,1 <= K <= 1000
  对于100%的数据,1 <= K <= N <= 100000 -100000 <= Ai <= 100000

输入格式

  第一行包含两个整数N和K。
  以下N行每行一个整数Ai。

  对于40%的数据,1 <= K <= N <= 100
  对于60%的数据,1 <= K <= 1000
  对于100%的数据,1 <= K <= N <= 100000 -100000 <= Ai <= 100000

评测已经全部AC

 

题后分享

刚开始看到这个题目的时候以为很简单,就排了序然后取最大的,最后做完发现这道题也是有坑的,要考虑的方面也很多,稍微仔细看下,还是很容易理解的,下面来讲解该怎么做这一题。

解析

根据所给序列寻找最大乘积一共有10种不同的找法,认真看其实并不难,找法很简单而且易懂,下面说一说有哪10种


  1. N个数全为正                                               取奇数个    
  2. N个数全为正                                               取偶数个
  3. N个数全为负                                               取奇数个
  4. N个数全为负                                               取偶数个
  5. N个数中一个正数多个负数                         取奇数个 
  6. N个数中一个正数多个负数                         取偶数个
  7. N个数中一个负数多个正数                         取奇数个 
  8. N个数中一个负数多个正数                         取偶数个
  9. N个数中多个正数多个负数                         取奇数个 
  10. N个数中多个正数多个负数                         取偶数个

在写程序的时候把每一种可能都写到程序里,自然就是一个无bug的程序。

将输入的N个数据保存到长度为N的a数组中,不管是哪一个可能,都要先对a数组进行升序排序,下面讲讲每一种可能应该怎么去找最大乘积

  1. N个数全为正,取奇数个,直接从a数组后面取K个数
  2. N个数全为正,取偶数个,直接从a数组后面取K个数
  3. N个数全为负,取奇数个,直接从a数组后面取K个数
  4. N个数全为负,取偶数个,直接从a数组前面取K个数
  5. N个数中一个正数多个负数,取奇数个,从a数组前面取K-1个数,a数组后面取一个数
  6. N个数中一个正数多个负数,取偶数个,直接从a数组前面取K个数
  7. N个数中一个负数多个正数,取奇数个 ,直接从a数组后面取K个数
  8. N个数中一个负数多个正数,取偶数个 ,直接从a数组后面取K个数
  9. N个数中多个正数多个负数,取奇数个,先把最大的那个正数a[n-1]留着,接着头与尾两两比较,乘积大的选取,乘积小的不选取,选取完第K-1个时,即可停止,最后一个选a[n-1],看下面的代码会比较好理解些!
  10. N个数中多个正数多个负数,取偶数个,头与尾两两比较,乘积大的选取,乘积小的不选取,直到选取K个

讲一个该题目挖的坑,因为-100000<=Ai<=100000 Ai*Ai最大可达到10的11次方,已经超过了int型的范围,所以a数组要用long型,下面直接上代码。

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        long[] num = new long[k];
        long[] a = new long[n];
        for (int i = 0; i < n; i++)
            a[i] = sc.nextInt();
        Arrays.sort(a);
        //负数个数
        int countfu = 0; 
        for (int i = 0; i < n; i++)
            if (a[i] < 0)
                countfu++;
        //正数个数
        int countz = n - countfu; 

        //全为正(取奇/偶数个)、全为负且取奇数个、一负多正(取奇/偶数个)、
        if (countfu == 0 || countfu == n && k % 2 == 1 || countfu == 1) 
            for (int i = 0; i < k; i++)
                num[i] = a[n - 1 - i];

        //全为负且取偶数个、一正多负且取偶数个
        if (countfu == n && k % 2 == 0 || countz == 1 && k % 2 == 0) { 
            for (int i = 0; i < k; i++)
                num[i] = a[i];
        }

        //一正多负且取奇数个
        if (countz == 1 && k % 2 == 1) { 
            num[0] = a[n - 1];
            for (int i = 1; i < k; i++)
                num[i] = a[i - 1];
        }

        //多正多负且取偶数个(两两一对,按对取)
        if (countz > 1 && countfu > 1 && k % 2 == 0) { 
            int i = 0, j = n - 1, x = 0;
            for (int p = 0; p < k / 2; p++) {
                if (a[i] * a[i + 1] > a[j] * a[j - 1]) {
                    num[x++] = a[i++];
                    num[x++] = a[i++];
                } else {
                    num[x++] = a[j--];
                    num[x++] = a[j--];
                }
            }
        }

        //多正多负且取奇数个(先把最大的正数留出来)
        if (countz > 1 && countfu > 1 && k % 2 == 1) { 
            int i = 0, j = n - 2, x = 0;
            for (int p = 0; p < k / 2; p++) {
                if (a[i] * a[i + 1] > a[j] * a[j - 1]) {
                    num[x++] = a[i++];
                    num[x++] = a[i++];
                } else {
                    num[x++] = a[j--];
                    num[x++] = a[j--];
                }
            }
            num[x] = a[n - 1];
        }

        long res = 1;
        for (int i = 0; i < k; i++) {
            res *= num[i];
            res %= 1000000009;
        }
        System.out.println(res);
    }

}

这道题目说难也不难,说简单也不简单,考虑的方面比较多,欢迎大家在下面留言,有问必答,大家继续努力吧!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值