“玲珑杯”ACM比赛 Round #23 C -- 你居然不吃巧克力(贪心+优先队列)

DESCRIPTION

给定一个正整数n,现在有n个石头,每个单独成一堆。
现在可以每次合并两堆石头,产生的能量为两堆石头个数的min。
你现在要将所有的石头合并成一堆,并且获得的能量最大。
输出这个最大值。
1 ≤ n ≤ 1e7。

INPUT
第一行是一个正整数T (1 ≤ T ≤ 10)表示数据组数,接下来T行每行一个正整数。数据满足一个测试点中,最多只有1个n超过1e6。
OUTPUT
T行,每行一个正整数。
SAMPLE INPUT
3135
SAMPLE OUTPUT
025

首先是最优策略的选取,如果想获得最大,那么肯定每次合并的两堆石头的价值差最小,那么将所有石头都加入优先队列,每次合并最小两堆即可

思路很好想到,主要是优化;

如果石子堆数是偶数的话,我们可以直接得到合并所能获得的最大价值

如果是奇数,那么将一堆石子入队,又会变为偶数

还有一些细节,具体看代码


#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define wtf printf("wtf\n");
using namespace std;
typedef long long ll;
const int N=1e7+20;
int a[N],n;
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        while(!q.empty())q.pop();
        scanf("%d",&n);
        int ans=0,m=n,val=1;
        while(m)
        {
            while(!q.empty())
            {
                int x=q.top();
                if(m>1&&x<val)//如果这堆石头价值大于优先队列中石头的价值,则有两种合并情况
                {
                    q.pop();
                    if(!q.empty())//优先队列中有比这堆石头还小的,那么优先队列中两个最小的合并
                    {
                        int y=q.top();
                        if(y<val)
                        {
                            q.pop();
                            q.push(x+y);
                            ans+=x;
                        }
                        else//否则优先队列中最小的和这堆石头合并
                        {
                            q.push(x+val);
                            ans+=x;
                            m--;
                        }
                    }
                    else//队列为空,优先队列中最小的和这堆石头合并
                    {
                        q.push(x+val);
                        ans+=x;
                        m--;
                    }
                }
                else//没有可以合并的,跳出循环
                    break;
            }
            if(m&1)
            {
                q.push(val);
                m--;
            }
            if(m>0)
            {
                ans+=m/2*val;
                m/=2;
                val<<=1;
            }
        }
        while(!q.empty())
        {
            int x=q.top();
            q.pop();
            if(q.empty())break;
            int y=q.top();
            q.pop();
            ans+=min(x,y);
            q.push(x+y);
        }
        printf("%d\n",ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值