(洛谷 1090 ssl 1040)合并果子

本文探讨了一种通过优化算法减少合并果子过程中体力消耗的方法。从最初的时间复杂度过高的算法出发,逐步改进到使用优先队列实现高效求解。通过对比不同算法的时间效率,展示了如何利用最小堆解决此类问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

分析:例如1 2 9 1和2合并成3 3和9合并成12 3+12为最小耗费值

 

于是就想到了一种朴素算法,快排一次把最小值合并再快排(时间复杂度o(n^2*log(n)))超时算法

 

  • #include <cstdio>
    #include <algorithm>
    using namespace std;
    int a[10001],n,ans;
    int main(){
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	while ((n--)>1){
    		sort(a+1,a+n+2); //排序
    		a[1]+=a[2]; ans+=a[1]; //最小值
    		for (int i=2;i<=n;i++) a[i]=a[i+1];
    	}
    	printf("%d",ans); return 0;
    }
  • 但是这样子是绝对有问题的。因为用了1.5*10^4毫秒(洛谷这样是绝对有问题的)
  • 第二个版本快排后+合并最小值(直接调换)(时间复杂度0(n^2+n log n))
var a:array[1..20000] of longint;    
    i,n,j,y,m,ans,x:longint;
procedure qsort(l,r:longint);
var i,j,mid,p:longint;
begin  
    i:=l; j:=r; mid:=a[(l+r) div 2];  
    repeat    
      while a[i]<mid do i:=i+1;     
      while a[j]>mid do j:=j-1;    
      if i<=j then    
      begin      
        p:=a[i];
        a[i]:=a[j]; 
        a[j]:=p;      
        i:=i+1;      
        j:=j-1;    
      end;  
      until i>j;  
      if l<j then qsort(l,j);   //分治
      if i<r then qsort(i,r);   //分治
end; 
begin   
   ans:=0;   
   readln(n);   
   for i:=1 to n do read(a[i]);   
   qsort(1,n);   
   for i:=1 to n-1 do   
   begin     
     x:=a[i]+a[i+1];     //最小值
     ans:=ans+x;     
     a[i+1]:=x;     
     for j:=i+1 to n-1 do     //依次交换
     begin       
       if a[j]>a[j+1] then      
       begin        
         m:=a[j];        
         a[j]:=a[j+1];        
         a[j+1]:=m;      
       end;    
     end;  
   end;  
   write(ans);
end.

3*10^3毫秒其实也是很多的

现在学c++ 偶然发现了优先队列(最小堆&最大堆)只用了88ms(I'm so happy)

#include <cstdio>
#include <queue> //队列
using namespace std;
priority_queue<int> q; //优先队列
int main(){
    int n,ans=0;
    scanf("%d",&n);
    for (int i=0,x;i<n;i++){
        scanf("%d",&x);
        q.push(-x); //默认最大堆,所以要反过来变成最小堆(大的反而小)
    }
    for (int i=1,tmp;i<n;i++){
        tmp=q.top(); 
        ans-=q.top();//一个数减另一个数等于加上它的相反数(因为是负数)
        q.pop(); //弹出
        tmp+=q.top();
        ans-=q.top();//一个数减另一个数等于加上它的相反数(因为是负数)
        q.pop();//弹出
        q.push(tmp);//重新插入
    }
    return !printf("%d",ans); //=(printf("%d",ans);return 0;)
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值