A - 最大乘积 - 牛客练习赛18

链接:https://www.nowcoder.com/acm/contest/110/A
来源:牛客网

题目描述
这题要你回答T个询问,给你一个正整数S,若有若干个正整数的和为S,则这若干的数的乘积最大是多少?请输出答案除以2000000000000000003(共有17 个零) 的余数。
举例来说,当 S = 5 时,若干个数的和为 5 的情形有以下 7 种(不考虑数字的顺序的话):
1. 1 + 1 + 1 + 1 + 1
2. 1 + 1 + 1 + 2
3. 1 + 1 + 3
4. 1 + 2 + 2
5. 1 + 4
6. 2 + 3
7. 5
他们的乘积依序为:
1. 1 * 1 * 1 * 1 * 1 = 1
2. 1 * 1 * 1 * 2 = 2
3. 1 * 1 * 3 = 3
4. 1 * 2 * 2 = 4
5. 1 * 4 = 4
6. 2 * 3 = 6
7. 5 = 5
其中乘积最大的是 2 * 3 = 6。
输入描述:
输入的第一行有一个正整数 T,代表该测试数据含有多少组询问。
接下来有 T 行,每个询问各占 1 行,包含 1 个正整数,代表该询问的 S 值。
输出描述:
对于每个询问,请输出答案除以 2000000000000000003(共有17个零) 的余数。
示例1
输入

10
1
2
3
4
5
6
7
8
9
100
输出

1
2
3
4
6
9
12
18
27
7412080755407364
备注:
1 ≤ T ≤ 100
1 ≤ S ≤ 2000
[分析]
先给出简单的思路
找规律发现无论n为多少,乘积都由2和3组成。

[代码]

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
#define mod 2000000000000000003
ll s,T,ans;
int main(void)
{
    scanf("%lld",&T);
    while(T--)
    {
        ans=1ll;
        scanf("%lld",&s);
        while(s>4)
            ans*=3ll,ans%=mod,s-=3;ans*=s,ans%=mod;
        printf("%lld\n",ans);
    }
    return 0;
}

[分析]
蛮早就想出了差不多的思路了。当时都差那么一点点,以至于写到后面有了各种解题思路。才ac…….

写到最后会发现其实所有合法乘积全是2和3组成的。这个思路是可以写的,但是我觉得,一开始做要发现这个规律有点难。
所以我这里提供另外一种理所当然的想法。

首先一上来能想到的是乘积里面的数字肯定都是两个相近的数字(无论有多少个)。为什么呢?
举个例子,将12分解,1 * 11必定小于6 * 6,你也可以自己多试几个数。
可以得到结论,相互接近的数字相乘更大。

现在的问题就是最大的那个乘积长度是多长。
这是一个很麻烦的问题,因为你不能将答案%MOD后去比较大小,虽然不会超时,但是取模后大小就变了。
所以需要精准地找出长度。
于是我试了几个数字,发现长度有迹可循。长度=(n+1)/3;

剩下的问题就比较简单了,乘积里面的具体是哪两个数字。取平均值就好了嘛!
如果平均值是整数那就只有一个数了。
如果平均值是小数,那么肯定是向上取整和向下取整两个数。
然后你就发现了。。。。无论n是多少,这两个数字都是2和3

还有一个问题,2和3分别有多少个。这个很简单,反正3尽量多嘛。实现的方法很多,这里提供大家一个思路,但是我有点讲不清楚。
2的数量=平均数*长度-n;

然后累乘就好了。

感觉写的不是很清晰,大家多磨磨吧。

[需要注意的地方]
这个题……..用快速幂会爆ll…………..我真的醉了,用了高级一点的方法反而不如暴力,卡了我好久。直到我测了2000这个数据。发现快速幂是个负数。

[代码]

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define MOD 2000000000000000003
typedef long long ll;

int main()
{
    ll n;
    int t;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%lld", &n);
        if (n == 1)
        {
            printf("1\n");
            continue;
        }

        ll i = (n + 1) / 3;
        ll avgg = n / (double)i + 0.999999;
        ll num = avgg*i - n;
        ll now=1;//= find(avgg, i - num) % MOD;
        for (ll j = 0; j < i-num; j++)
        {
            now = (now*avgg) % MOD;
        }
        for (ll j = 0; j < num; j++)
        {
            now = (now*(avgg - 1)) % MOD;
        }

        printf("%lld\n", now%MOD);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值