NOIP2004Day1P2合并果子

例如有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永葆青春。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值