P8669 [蓝桥杯 2018 省 B] 乘积最大

2/26更新:

一开始的做法忽略了一点:

题目中强调的是“请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以 $1000000009$(即 $10^9+9$)的余数”,而不是“取余后最大的乘积”。

整体思路:

先把arr数组按照绝对值降序排序,我们默认把前k个数字的乘积作为ans,如果这个ans是正数,那么直接输出即可;

如果它是负数,就看能不能调整成正数,很显然,每替换一个数字,这个ans都会变小,所以我们只需要进行最多一次替换即可;

如果不能替换成正数的话,那么ans就是后k个数字的乘积。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <cmath>
#define For(i, j, n) for (int i = j; i <= n; ++i)
using namespace std;
typedef long long LL;

const int N = 1e5 + 5;
const double INF = -1e10;
const LL mod = 1e9 + 9;

int n, k;
LL a[N];

bool cmp(int a, int b)
{
    return abs(a) > abs(b);
}

LL get_min()
{
    LL ans = 1;
    for (int i = n; i >= n - k + 1; i--)
    {
        ans = ans * a[i] % mod;
    }
    return ans;
}

LL get_max(int b, int tar)
{
    LL ans = 1;
    for(int i = 1; i <= k; i++)
    {
        if(i != b)
        {
            ans = ans * a[i] % mod;
        }
    }
    if(b != -1)
        ans = ans * a[tar] % mod;
    return ans;
}

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    sort(a + 1, a + n + 1, cmp);
    int cnt = 0, p1 = -1, p2 = -1, m1 = -1, m2 = -1;
    LL ans = 1;
    for (int i = 1; i <= k; i++)
        if (a[i] < 0)
            cnt++;
    if (cnt & 1) // 绝对值最大的k个数字只能得到负数
    {
        for (int i = k; i; i--)
        {
            if (p1 != -1 && m1 != -1)
                break;
            if (p1 == -1 && a[i] > 0)
                p1 = i;
            if (m1 == -1 && a[i] < 0)
                m1 = i;
        }
        for (int i = k + 1; i <= n; i++)
        {
            if (p2 != -1 && m2 != -1)
                break;
            if (p2 == -1 && a[i] > 0)
                p2 = i;
            if (m2 == -1 && a[i] < 0)
                m2 = i;
        }
        double f1 = INF, f2 = INF;
        if(m1!=-1&&p2!=-1)
            f1 = (double)a[p2]/(double)(-a[m1]);
        if(p1!=-1&&m2!=-1)
            f2 = (double)(-a[m2])/(double)a[p1];
        if (f1==INF&&f2==INF) // 无法得到正数
        {
            ans = get_min();
        }
        if(f1 > f2)
            ans = get_max(m1, p2);
//-------------------------------------------------------------------------------------------------------
        else if(f1 < f2) //这里不能只写if,否则在无法得到正数的情况下,会把答案从get_min替换成这个get_max
            ans = get_max(p1, m2);
//-------------------------------------------------------------------------------------------------------
    }
    else
        ans = get_max(-1, -1);
    printf("%lld\n", ans);
    return 0;
}

 

这是一开始的做法,死活有两个点过不去:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <cmath>
#define For(i, j, n) for (int i = j; i <= n; ++i)
using namespace std;
typedef long long LL;

const int N = 1e5 + 5;
const double INF = 1e10;
const LL mod = 1e9 + 9;

int n, k;
LL a[N];

bool cmp(int a, int b)
{
    return abs(a) > abs(b);
}

LL get_min()
{
    LL ans = 1;
    for (int i = n; i >= n - k + 1; i--)
    {
        ans = ans % mod * a[i] % mod;
    }
    return ans;
}

LL get_max(int b, int tar)
{
    LL ans = 1;
    for(int i = 1; i <= k; i++)
    {
        if(i != b)
        {
            ans = ans % mod * a[i] % mod;
        }
    }
    if(b != -1)
        ans = ans  % mod * a[tar] % mod;
    return ans;
}

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    sort(a + 1, a + n + 1, cmp);
    int cnt = 0, p1 = -1, p2 = -1, m1 = -1, m2 = -1;
    LL ans = 1;
    for (int i = 1; i <= k; i++)
        if (a[i] < 0)
            cnt++;
    if (cnt & 1) // 绝对值最大的k个数字只能得到负数
    {
        for (int i = k; i; i--)
        {
            if (p1 != -1 && m1 != -1)
                break;
            if (p1 == -1 && a[i] > 0)
                p1 = i;
            if (m1 == -1 && a[i] < 0)
                m1 = i;
        }
        for (int i = k + 1; i <= n; i++)
        {
            if (p2 != -1 && m2 != -1)
                break;
            if (p2 == -1 && a[i] > 0)
                p2 = i;
            if (m2 == -1 && a[i] < 0)
                m2 = i;
        }
        bool f1 = true, f2 = true;
        if(m1 != -1 && p2 != -1)
        {
            f1 = false;
            ans = get_max(m1, p2);
        }
        if(p1 != -1 && m2 != -1)
        {
            f2 = false;
            ans = max(ans, get_max(p1, m2));
        }
        if (f1 && f2) // 无法得到正数
        {
            ans = get_min();
        }
    }
    else
        ans = get_max(-1, -1);
    printf("%lld\n", ans);
    return 0;
}

这是贪心正解:

我们可以先考虑只有正数的情况,这时候只要选最大的数就行了;

而引入负数之后,因为负负得正,有可能会把原来最小的给反转成最大的,那么这时候,我们只要再把这个反转的可能性考虑进来即可,实现方式就是既考虑最大的那一头,也考虑最小的那一头。

#include<bits/stdc++.h>
#define int long long 
#define mod 1000000009
using namespace std;

int n,k,a[100005],ans;

signed main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1);
    ans=k&1?a[n]:1;
    n-=k&1;
    k-=k&1;
    int l=1,r=n,f=ans<0?-1:1;
    while(k)
    {
        int p=a[l]*a[l+1],q=a[r]*a[r-1];//两端取
        if(p*f>q*f)//贪心选取
        {
            ans=p%mod*ans%mod;
            l+=2;
        }
        else
        {
            ans=q%mod*ans%mod;
            r-=2;
        }
        k-=2;
    }
    cout<<ans;
     return 0;
}

 

posted @ 2024-02-25 21:59  Gold_stein  阅读(7)  评论(0编辑  收藏  举报