HDU 4038 Stone(11年成都网络赛-H题-贪心)

题目链接:Click here~~

题意:

给一个数列{ai},有两种操作:1、选取数列中的一个数并给它加1。

                                               2、在数列中添加一个数字1。

问如何进行 m 次操作,使数列各项乘积 p 的值最大。

解题思路:

由于数列中有正负数和0,所以导致情况很多,让我们逐个来考虑。
1、负数个数为奇数。(p<=0)
     如果数列中不存在0,那么乘积 p 一定为负。不难想到,最好的方法是选绝对值最小的那个负数,把它加到0,使 p 变成 0。
     此时进入情况2。
2、数列中含有0。(p=0)
     要想把p上升成正数,显然需要把所有的0变成1。
     然后进入情况3。
3、负数个数为偶数。(p>0)
     首先,肯定不动负数,因为给负数加1,会使其绝对值减小,即 p 会减小。
     而且,如果进行操作1,我选的那个数一定是当前数列中最小的正数,显然这样会使 p 变得更大。
     如果进行操作2,在不是毫无办法的情况下,我绝对不会傻傻的只添加个1,因为那样对 p 没有贡献。
     也就是说,我会一直把它添加到x(x>=2),即操作2一定会和操作1结合起来使用,为了方便,我们把这些结合起来的操作都看做是操作2。

     另外,我不会选择进行操作2后再去进行操作1。因为操作2新添加的数,不会超过之前数列中最小的正数。

     下面,对于那些正数,比较操作1 和操作2 哪个更划算。
     首先,当数列中含有1的时候,一定选操作1。因为把1个1变成2,只需要消耗1步操作就可以达到和消耗2步操作2 相同的效果。
     于是我们考虑不含1的数列,比如{a,b,c,d}(按从小到大的顺序排列,a>=2),操作步数为 m。
             当m=1 时:操作 1 更佳。
             当m=2 时:①a=b。△1 = (2b+1)*cd,△2 = b^2*cd。令△2 < △1,得 b <= 2。又a=b,得唯一解 a = b = 2。
                               ②a<b。△1 = 2b*cd,△2 = a*b*cd。由于a >= 2,所以△2 >= △1。且当 a=2 时,等号成立。

                               综上,当 a=2 时,操作1 更佳,否则 操作2 更佳。

                               推论:把所有的2变成3不会使结果更差。

             当m>2时:不会证明了。。。囧。。。
     最后还剩下的操作就靠下面这个结论了:

——————————————————————————————分割线——————————————————————————————

Q:将一个正整数 x 分解成 k 个正数 ai 相加的形式,问如何才能使得到的各个 ai 的乘积 S 最大(i:1~k , k任意)。


结论1:ai > 1。

     如果ai  = 1,显然它对乘积 S 没有贡献,不如把它随便加到其他的一个数上,得到的结果一定更大。

结论2:ai < 4。

     第一种证明方法:

            反证法,假设有某个ai>=4,那么如果把 ai 分成 2+(ai-2) 的形式,得到的乘积 2*ai-4 = ai + (ai-4) >= ai,肯定不会使结果更差。

     第二种证明方法:

            设任意正整数a、b,考虑什么时候分解会使结果更大,令 a*b >= a+b,即 (a-1)*(b-1) >= 1,可推出 a>=2,b>=2,且当 a=b=2 时等号成立。

            所以如果有 ai>=4,那么一定可以找到那样的整数a、b,使得 a*b >= a+b,也不会使结果更差。

结论3:ai 中 2 的个数不超过 2 个。

     反证法,假设 2 的个数大于等于3个,那么 3个2 不如 2个3 得到的乘积大。


综上,若 x = 3*k     ,ans = 3^k;

         x = 3*k + 1,ans = 3^(k-1) * 2*2; 

         x = 3*k + 2,ans = 3^k * 2。

——————————————————————————————分割线——————————————————————————————

#include <vector>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

typedef __int64 LL;

const int mod = 1000000007;

int mypow(int a,LL n)
{
    int ans = 1;
    for(LL t=a;n;n>>=1)
    {
        if(n&1)
            ans = (ans*t) % mod;
        t = (t*t) % mod;
    }
    return ans;
}

int main()
{
//    freopen("in.ads","r",stdin);
//    freopen("ans.ads","w",stdout);
    int z,n,x,ncase = 0;
    vector<int> NV,PV;
    scanf("%d",&z);
    LL m,cnt[4];
    while(z--)
    {
        memset(cnt,0,sizeof(cnt));
        scanf("%d%I64d",&n,&m);
        bool neg = false;
        int minPos = 10001,pid;
        int maxNeg =-10001,nid;
        NV.clear();
        PV.clear();
        while(n--)
        {
            scanf("%d",&x);
            if(x >= 0)
            {
                if(x < 4)
                {
                    ++cnt[x];
                    continue;
                }
                PV.push_back(x);
                if(x < minPos)
                    minPos = x , pid = PV.size()-1;
            }
            else
            {
                NV.push_back(x);
                if(x > maxNeg)
                    maxNeg = x , nid = NV.size()-1;
                neg ^= 1;
            }
        }
        if(neg)                         //把最大的负数加成0
        {
            if(NV[nid]+m >= 0)
            {
                m += NV[nid];
                cnt[0]++;
                NV.erase(NV.begin()+nid);
            }
            else
            {
                NV[nid] += m;
                m = 0;
            }
        }
        if(m >= cnt[0])                 //把0加成1
        {
            m -= cnt[0];
            cnt[1] += cnt[0];
            cnt[0] = 0;
            if(m <= cnt[1])             //把1加成2
            {
                cnt[2] += m;
            }
            else
            {
                m -= cnt[1];
                cnt[2] += cnt[1];
                if(m <= cnt[2])         //把2加成3
                {
                    cnt[2] -= m;
                    cnt[3] += m;
                }
                else
                {
                    m -= cnt[2];
                    cnt[3] += cnt[2];
                    cnt[2] = 0;
                    if(m == 1)
                    {
                        if(cnt[3])
                            cnt[3]-- , PV.push_back(4);
                        else
                            if(!PV.empty())
                                PV[pid]++;
                    }
                    else
                    {
                        if(m%3 == 0)
                        {
                            cnt[3] += m/3;
                        }
                        else if(m%3 == 1)
                        {
                            cnt[3] += m/3-1;
                            cnt[2] += 2;
                        }
                        else
                        {
                            cnt[3] += m/3;
                            cnt[2] += 1;
                        }
                    }

                }
            }
        }
        LL ans = 1;
        if(!cnt[0])
        {
            for(int i=2;i<=3;i++)
                ans = (ans*mypow(i,cnt[i])) % mod;
            for(int i=0;i<NV.size();i++)
                ans = ans*NV[i] % mod;
            for(int i=0;i<PV.size();i++)
                ans = ans*PV[i] % mod;
        }
        printf("Case %d: %I64d\n",++ncase,cnt[0] ? 0 : ans);
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值