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;
}