NOIP2004Day1P2合并果子

本文介绍了一个关于最小体力耗费的贪心算法问题,详细解释了算法思想,并提供了AC*2代码实现。通过分治策略和使用优先队列(STL中的优先队列)进行优化,解决了一个涉及数组合并和最小体力消耗的问题。

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

例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

输入输出格式

输入格式:

输入文件fruit.in包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。

输出格式:

输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。

输入样例

3
1 2 9


输出样例

15


说明

对于30%的数据,保证有n<=1000:

对于50%的数据,保证有n<=5000;

对于全部的数据,保证有n<=10000。

此题为贪心。证明如下:

分治来考虑,问题可以划分为子问题 a1<a2<a3的合并顺序。

可以知道合并第二次的和是一样大的,所以只需要让第一次合并和最小,所以要先合并a1,a2。

所以,问题转化为在数组中寻找到第一,第二小的数,合并,放回去,不断重复这个过程。取一次拍一次序必超时,所以考虑使用堆这个数据结构来处理。而用堆,最好使用STL中的优先队列,这样节约考场时间,又不容易出错。

代码如下:

#include
  
   
#include
   
    
#include
    
     
using namespace std;
int n,ans;
priority_queue 
     
      
       ,greater
       
         > fruit; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int temp; scanf("%d",&temp); fruit.push(temp); } ans=0; for(int i=1;i
        
       
      
     
    
   
  

我在构造堆时使用了greater仿函数,目的是构造小根堆,因为默认是大根堆。


这里引用洛谷的yyy2015c01 同学的非STL做法,感谢感谢!!!!!

读进数组

排序 取a,b(b开始为空,初始化为近似无穷大,但要使得“无穷大”+“无穷大”=“无穷大”*2,而不会溢出成为负数)

取a,b中最小的两个数合并,存在b数组后面,相加的两个数删掉。这两个数的和累加到体力中。该步重复n-1次。

输出体力

AC*2代码:


#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cctype>
#include<iostream>
using namespace std;
int a[100001],b[100000],al=0,ar,bl=0,br=0,tl=0;
void pd()
{
if((a[al]+a[al+1] < a[al]+b[bl])&& (a[al]+a[al+1] < b[bl]+b[bl+1]))
{
b[br]=a[al]+a[al+1];
al+=2;
}
else
{
if (a[al]+b[bl] < b[bl]+b[bl+1])
{
b[br]=a[al]+b[bl];
bl++;
al++;
}
else
{
b[br]=b[bl]+b[bl+1];
bl+=2;
}
}
tl+=b[br];
br++;
}
int main()
{
memset(a,0x3f3f3f3f,sizeof(int)*100001);
memset(b,0x3f3f3f3f,sizeof(int)*100000);
int n;
scanf("%d",&n);
for (int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
ar=n-1;
int i=1;
bool jh=false;
do
{
jh=false;
for (int j=1;j<=n-i;j++)
{
if (a[j-1]>a[j])
{
a[100000]=a[j-1];
a[j-1]=a[j];
a[j]=a[100000];
jh=true;
}
}
i++;
}
while(jh);
for (int i=1;i<n;i++)
{
pd();
}
printf("%d",tl);
}
祝大家学习愉快,愿OI永葆青春。

### NOIP 2004 提高组 合并果子 Python 解题思路 #### 背景描述 合并果子问题是经典的贪心算法题目之一。给定若干果子的数量,每次可以选取两数量最少的果子将其合并成一,并记录此次合并所花费的成本(即这两果子数之和)。最终目标是最小化总成本。 #### 思路分析 为了最小化合并过程中的总成本,应该优考虑将较小的两合并起来。这样做的好处是可以减少后续较大规模合并时所需付出的成本。具体来说: - 使用一个小根来存储每果子的数量。 - 每次取出两个最小值进行合并操作,并把得到的结果重放回中继续参与下一轮比较。 - 记录每一次合并产生的费用直到只剩下一为止[^1]。 #### 实现方法 基于上述策略,在Python编程语言环境下可以通过`heapq`模块轻松构建这样一个高效的小顶结构来进行求解。 ```python import heapq def min_cost_to_merge_fruits(fruit_piles): # 将所有的果子加入到一个列表里, 并转换为最小 heapq.heapify(fruit_piles) total_cost = 0 while len(fruit_piles) > 1: # 取出当前最小的两果子 first_min = heapq.heappop(fruit_piles) second_min = heapq.heappop(fruit_piles) current_cost = first_min + second_min # 更总的消耗代价 total_cost += current_cost # 把这次合并后的结果再加回到里面去 heapq.heappush(fruit_piles, current_cost ) return total_cost ``` 此函数接收一个整型数组作为输入参数,代表初始状态下各果子的具体数目;返回的是完成全部合并不需要额外空间复杂度下的最低可能耗费时间/次数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值