【牛客网】幸运的袋子--深度解剖

题目:幸运的袋子

一个袋子里面有n个球,每个球上面都有一个号码(拥有相同号码的球是无区别的)。如果一个袋子是幸运的当且仅当所有球的号码的和大于所有球的号码的积。
例如:如果袋子里面的球的号码是{1, 1, 2, 3},这个袋子就是幸运的,因为1 + 1 + 2 + 3 > 1 * 1 * 2 * 3
你可以适当从袋子里移除一些球(可以移除0个,但是别移除完),要使移除后的袋子是幸运的。现在让你编程计算一下你可以获得的多少种不同的幸运的袋子。

示例1

输入:【第一行输入一个正整数n(n ≤ 1000) 第二行为n个数正整数xi(xi ≤ 1000)】

3 1 1 1

输出:【输出可以产生的幸运的袋子数】

2

分析:

给一个案例:{ 1,1,3,4,9 };

一般情况下,我们第一反应就是从后面开始拿走,先拿掉 9,再比较和与积谁大,不是幸运的再继续拿掉 4 。这种方法可以,但是代码写起来比较繁琐。

我们相反的思路来看看,比如从1开始,一个一个往里面加球,先是 11,113,1134,4 已经不幸运了,就说明 1139 更加不可能,113 已经结束,所以要回退到 11 开头,这里我们回退的话需要减去之前的值,现在是1134,所以要减去 3 和 4 ,接着到114,1149,不幸运,回退,减去 4 和 9 ,再到 119,到 119 了,说明 11 开头的就已经结束了,需要减 1 ,到 1 开头,即 13 开头、14 开头、19 。

其中重点有两个:

一个是为什么是 1 :为什么开头都是 1 呢?因为只有 1 开头的袋子,才有幸运袋子,比如 {1 2},{1 9},如果是 2 或者以上,{2 2},{9 9},2+2 > 2*2 吗?2 开头都不行了,后面就更加不用说了,所以第一个球必定是 1。

第二,去重问题:这个问题非常关键,我们画个图

 图上已经很明确了,如果下一个数和自己是相同的,那就说明已经有过相同的案例了,所以直接跳过,或

假设案例是 { 1 1 3 3 9 },11,113,1133,1139,然后到达 1 开头第一个 3 到 133 ,再到 139 ,注意这里已经有一次 139 了,这时第一个 3 结束,轮到下一个数 3, 第一个就是 139 ,但是 139 在第一个 3 就已经出现了,所以为了去重,我们遇到下一个相等的就跳过。

下面来看代码:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int  LuckyNum(vector<int>& vv, int n, int pos, int sum, int multi)
{
    //数组, n, 当前位置, 次数, 和, 积
    int count = 0;
    //这里的 for 循环其实只走第一个数:1,因为其他开头的都是不幸运的,上面分析过,
    //还有去重,即使第二个是 1 ,也会直接跳过
    for (int i = pos; i < n; i++)
    {
        sum += vv[i];
        multi *= vv[i];
        //判断幸运
        if (sum > multi)
        {
            count += 1 + LuckyNum(vv, n, i+1, sum, multi);
        }
        else if (vv[i] == 1)
            //因为袋子至少要有2个,所以第一个 1 时,直接跳到下一层
            count += LuckyNum(vv, n, i+1, sum, multi);
        else
            break;
        //回退,减和除去当前值
        sum -= vv[i];
        multi /= vv[i];
        
        //去重,+1跳过
        while (i < n-1 && vv[i] == vv[i+1])
            i++;
    }
    
    return count;
}

int main()
{
    int n, m, sum, multi, count = 0;
    while (cin >> n)
    {
        vector<int> vv(n);
        for (int i = 0; i < n; i++)
        {
            cin >> vv[i];
        }
        //我们要排序,才能逐个计算
        sort(vv.begin(), vv.end());
        cout << LuckyNum(vv, n, 0, 0, 1) << endl;
    }
    
    return 0;
}

是否已经理解清楚。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值