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

2/26更新:

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

题目中强调的是“请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以 1000000009(即 109+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;
}
复制代码

 

本文作者:Gold_stein

本文链接:https://www.cnblogs.com/smartljy/p/18033177

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Gold_stein  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑
  1. 1 逃离地面 RAD & 三浦透子
逃离地面 - RAD & 三浦透子
00:00 / 00:00
An audio error has occurred.

作词 : 野田洋次郎

作曲 : 野田洋次郎

空飛ぶ羽根と引き換えに 繋ぎ合う手を選んだ僕ら

それでも空に魅せられて 夢を重ねるのは罪か

夏は秋の背中を見て その顔を思い浮かべる

憧れなのか、恋なのか 叶わぬと知っていながら

重力が眠りにつく 1000年に一度の今日

太陽の死角に立ち 僕らこの星を出よう

彼が眼を覚ました時 連れ戻せない場所へ

「せーの」で大地を蹴って ここではない星へ

行こう

もう少しで運命の向こう もう少しで文明の向こう

もう少しで運命の向こう もう少しで

夢に僕らで帆を張って 来たるべき日のために夜を超え

いざ期待だけ満タンで あとはどうにかなるさと 肩を組んだ

怖くないわけない でも止まんない

ピンチの先回りしたって 僕らじゃしょうがない

僕らの恋が言う 声が言う

「行け」と言う