合并果子
参考博客:
https://blog.csdn.net/shao824714565/article/details/52184869
https://blog.csdn.net/qq_40512553/article/details/78190998
https://blog.csdn.net/weixin_36888577/article/details/79937886
https://blog.csdn.net/zzzanj/article/details/80979552
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。 例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
输入要求
输入包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。
输出要求
输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31
样例输入
3
1 2 9
样例输出
15
思路分析
保证消耗体力最小:每次合并的果子数量是最小和次小的。
解决方式
1.不排序查找(排序会浪费大量时间)最小和次小的数据,记录下其所需体力消耗,在原数据堆中删除此两数据,加入合并后的新数据。
2.优先队列
涉及知识点及其链接
贪心算法:https://baike.baidu.com/item/贪心算法/5411800?fr=aladdin
优先队列:https://baike.baidu.com/item/优先队列/9354754?fr=aladdin
STL库:https://baike.baidu.com/item/STL/70103
c++优先队列(priority_queue)用法详解:https://blog.csdn.net/weixin_36888577/article/details/79937886
AC代码:
解决方法1:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 10010
int num[MAX];
int n;
void Swap(int a,int b)//更改在数组中的两个元素的位置
{
int emp;
emp = num[a];
num[a] = num[b];
num[b] = emp;
}
void Fmin(int x)//x为数组下标
{
int i,p;
p = x;
for(i = p + 1 ; i <= n; i++)//向下标位置之后检索
{
if(num[i] < num[p])
{
p = i;//用p记录最小位置下标
}
}
Swap(x,p);
}
int main()
{
int i,j;
long int sum = 0;
memset(num,0,sizeof(num));
scanf("%d",&n);
for(i = 1 ; i <= n ; i++)
{
scanf("%d",&num[i]);
}
Fmin(1);
Fmin(2);
//检索初始化
for(i = 2; i <= n ;i++)
{
num[i] += num[i-1];//合并果子
sum += num[i];//记录体力消耗
Fmin(i);
Fmin(i+1);
}
printf("%d\n",sum);
return 0;
}
解决方法2:
#include<bits/stdc++.h>
using namespace std;
int n,a;
int x,ans;
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&a);
q.push(a);//推入队列
}
for(int i = 1;i < n; i++)
{
x = 0;
x += q.top();//加入顶上最小的数据
q.pop();//推出最小数据
x += q.top();//加入顶上次小的数据
q.pop();//推出次小的数据
ans += x;//合并最小数据和次小数据所消耗的体力
q.push(x);//将两堆果子合并后放回原队列
}
printf("%d",ans);
return 0;
}
错误原因分析:
1.受输入样例影响,排序一次后就将原数据简单相加,忽略合并后的果子并不一定就是最小的果子堆,需要重新排序。
2.用单独、简单的排序方法更新每次合并之后剩余的数据堆,会浪费大量时间引起超时。
可考虑引入插入排序的方法,具体参考:https://blog.csdn.net/zzzanj/article/details/80979552