jozj4010-我才不是萝莉控呢【哈夫曼树】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/86577658

正题


题目大意

( n , 1 ) (n,1) ( 1 , 1 ) (1,1) ,一个数组 A A ,满足 A i A i + 1 A_i\geq A_i+1
每次有两个选择走到 ( x 1 , y + 1 ) (x-1,y+1) ,或 ( x , y / 2 ) (x,\lfloor y/2\rfloor) 。后者需要消耗 i = x n A i \sum_{i=x}^nA_i 的代价
求最小代价


解题思路

先预处理好 B x = i = x n A i B_x=\sum_{i=x}^nA_i
很容易推出动态转移方程
f i , j = m i n { f i 1 , j + 1 , f i , j 2 + B i } f_{i,j}=min\{f_{i-1,j+1},f_{i,j*2}+B_i\}
然后我们发现首先 A i A_i 是有序的,而 B i B_i 的性质
f i , j f_{i,j} 表示放入了前 i i 个叶子节点,有 j j 个空位的哈夫曼树权值。
然后每次可以加入一个叶子节点在该层,且以后合并代价增加 A i A_i
也可以将两个合并到新一层,空位多一些。
然后就愉快的发现这个的动态转移和之前的一样,其实就是哈夫曼树。
然后每次肯定是优先选择权值最小的合并,就是合并果子原题。


c o d e code

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

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/86577658