版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/86577658
正题
题目大意
从
到
,一个数组
,满足
每次有两个选择走到
,或
。后者需要消耗
的代价
求最小代价
解题思路
先预处理好
很容易推出动态转移方程
然后我们发现首先
是有序的,而
的性质
设
表示放入了前
个叶子节点,有
个空位的哈夫曼树权值。
然后每次可以加入一个叶子节点在该层,且以后合并代价增加
也可以将两个合并到新一层,空位多一些。
然后就愉快的发现这个的动态转移和之前的一样,其实就是哈夫曼树。
然后每次肯定是优先选择权值最小的合并,就是合并果子原题。
#include<cstdio>
#define ll long long
using namespace std;
ll a[100010],num,x,n;
long long s,u;
void up(ll x)
{
ll t;
while (x>1 && a[x]<a[x/2])
{
t=a[x];a[x]=a[x/2];a[x/2]=t;
x/=2;
}
}
void down(ll x)
{
ll t,y;
while (x*2<=num && a[x]>a[x*2] || x*2+1<=num && a[x]>a[x*2+1])
{
y=x*2;
if (x*2+1<=num && a[x*2]>a[x*2+1]) y++;
t=a[x];a[x]=a[y];a[y]=t;
x=y;
}
}
void insert(ll x)
{a[++num]=x;up(num);}
int main()
{
int t;
scanf("%lld",&t);
while(t--)
{
s=0;
scanf("%lld",&n);num=0;
for (ll i=1;i<=n;i++)
{
scanf("%lld",&x);
insert(x);
}
while (num>1)
{
u=a[1];a[1]=a[num];num--;down(1);
u+=a[1];a[1]=a[num];num--;down(1);
s+=u;num++;a[num]=u;up(num);
}
printf("%lld\n",s);
}
}