#####学习记录自用,欢迎批评指正
题目:
前情提要:小白兔在台阶上发现了亿堆胡萝卜,于是他又登上了台阶打算搬运一点胡萝卜。
但是,当他来到台阶顶时,发现胡萝卜都散乱的分成了好多堆,于是小白兔决定把所有的胡萝卜合并成一堆胡萝卜。但由于小白兔太小了,一次合并只能合并两堆胡萝卜。由于小白兔在合并完成之后,还要搬运一些胡萝卜回到幼稚园,所以他需要节省体力。那么如何合并所有胡萝卜堆可以使消耗的体力最小?
假设每个胡萝卜重量均为1,每堆胡萝卜的数量即为重量,消耗的体力与重量成正比,即,可以假定合并重量为1和3的胡萝卜堆消耗的体力为1+3 = 4
输入格式
输入包括两行,第一行是一个整数n,表示胡萝卜堆的堆数。
第二行包含n个整数,第i个整数 a[i]是第 i堆胡萝卜堆的数目。
n -> [1, 10^5]
a[i] -> [1, 10^9]
输出
输出包括一行,这一行只包含一个整数,即最小的体力耗费值。
只能使用C语言
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 以文本方式显示
| 以文本方式显示
| 1秒 | 1024KB | 0 |
思路:
这个题在洛谷其实是有原型的:基础版P1090,提升版P6033
但是洛谷的题解不少都用了C++的方法,学校的作业系统只允许使用C语言。
这里记录一下我的思路struggle历程。
以下是我在学校讨论区发布的内容:
整个题的思路就和之前的同学说的那样很简单,每次取最小值和次小值进行合并就可以。
但在如此大的数据量和数据大小之下怎么让程序有更高的效率、更快的速度是个问题。
所以最初我用了链表的方式,我的想法是,使用链表的话只需要在第一次进行数组排序,然后建立链表,在之后的合并过程中,合并后的元素只需要通过对之后元素的查找来按顺序插入就好,如果用数组的话数字的移动肯定会导致超时,但链表不需要移动,所以会更加节省时间。
但是事实是编译后有很多很多TLE…………
百度后理由如下:
结论:数组顺序遍历快于链表
原因:CPU每次取数据时都不是只取一个数据,而是将那连续的一块一起取,而因为数组的地址空间是连续的,所以下几次要访问的元素也一起被放入缓存中了,减少了对内存的访问(访问速度:缓存 快于 内存)。由于链表的元素是离散的放在内存中的,所以遍历每个元素的都需要对内存进行访问,导致顺序访问效率不如数组
原文链接:
https://blog.youkuaiyun.com/qq_45843451/article/details/124216812
OK,fine.我重写。
然后老老实实用数组写。这次的思路是不每次都进行排序,每次都只搜索当前数组中最小和次小的两个数字进行合并。一定程度上比每次都进行重新排序要快得多。但结果是最后三个TLE。
OK,fine.我再重写。
然后本蒟蒻参照了讨论区的方法——开两个数组,一个记录原始值,一个记录合并之后的新值的方法,进行比较,选取两个最小的进行合并的方法,最终AC了。但在这个过程中也有一些小插曲——我尝试着使用了桶排序的方法(不得不说还是很好用的)对数组进行第一次排序,但是可能是因为开的数组过大了(因为数据值过大, 要求数组的大小也大),这次直接全是TLE了。
总之就是很曲折,很曲折,方法要选对,要不然就是白搭。
这个题思路还是很明白的,在这儿就没必要重新梳理思路了,直接上代码吧。当然,最后AC的代码就不放出来了,毕竟这个课的代码要查重的。
首先是冤种链表方法:(这个方法在洛谷的P1090题提交是可以AC的,但是P6033就会超时,在作业提交系统中也会超时)
#include <stdio.h>
#include <stdlib.h>
//#include <time.h>
//double start=clock();
long long a[100001]={0},energy=0;
typedef struct link
{
long long num;
struct link *next;
}NODE;
int cmp(const void *a,const void *b){
long long n1=*(long long*)a;
long long n2=*(long long*)b;
return (n1-n2);
}
void setlink(NODE *h,int n)
{
NODE *p=NULL, *q=NULL;
int i,num;
for( i=1; i<=n; i++)
{
p = (NODE *)malloc(sizeof(NODE));
p->num = a[i];
p->next = NULL;
num=p->num;
if( h->next == NULL )
{
h->next = p;
q = p;
}
else
{
q->next = p;
q = q->next;
}
}
p->next=NULL;
/*printf("最初的链表");
for(p=h->next;p!=NULL;p=p->next)printf("%lld ",p->num);
printf("\n");
*/
return;
}
void sum(NODE *head)
{
NODE *p=head->next,*q=head->next->next;
q->num+=p->num;
energy+=q->num;
head->next=p->next;
/*printf("sum运行");
for(p=head->next;p!=NULL;p=p->next)printf("%lld ",p->num);
printf("\n");
printf("时间%f",clock()*/
free(p);
}
void change(NODE *head)
{
NODE *p=head->next,*q=head;
int insert=0;
if(head->next->next->next==NULL){
p->next->num+=p->num;
energy+=p->next->num;
head->next=p->next;
q=p->next;
free(p);
goto k;
}
if(p->num<=p->next->num)goto k;
else{
for(q=p->next;q->next->next!=NULL;q=q->next){
if(p->num >= q->num&&p->num <= q->next->num){
head->next=p->next;
p->next=q->next;
q->next=p;
insert=1;
break;
}
}
}
if(insert==0)
{
for(q=p->next;q->next!=NULL;q=q->next);
head->next=p->next;
q->next=p;
p->next=NULL;
}
k:;
/*printf("change运行");
for(p=head->next;p!=NULL;p=p->next)printf("%lld ",p->num);
printf("\n");
return;*/
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
if(n==1){
printf("0\n");
return 0;
}
else if(n==2){
printf("%lld\n",a[1]+a[2]);
return 0;
}
qsort(a,n+1,sizeof(long long),cmp);
/*for(int i=1;i<=n;i++)
{=
printf("%lld ",a[i]);
}*/
NODE *head=NULL, *q=NULL;
head = (NODE *)malloc(sizeof(NODE));
head->num = -1;
head->next = NULL;
setlink(head, n);
while(head->next->next!=NULL){
sum(head);
change(head);
}
printf("%lld\n",energy);
return 0;
}
接下来是寻找最小值和次小值的方法:(这个代码也可以在基础版AC,但进阶版仍然过不了)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
long long a[100001]={0},energy=0;
int n;
void find(int start)
{
int num=start;long long t;
for(int i=start;i<=n;i++){
if(a[i]<a[num])num=i;
}
t=a[num];a[num]=a[start];a[start]=t;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
if(n==1){
printf("0\n");
return 0;
}
else if(n==2){
printf("%lld\n",a[1]+a[2]);
return 0;
}
find(1);
find(2);
for(int i = 2; i <= n ;i++)
{
a[i]+=a[i-1];
energy+=a[i];
find(i);
find(i+1);
}
printf("%lld\n",energy);
return 0;
}
最后提到的桶排序的方法就不在这里赘述了,洛谷的题解中有详细解释。
以上贴出的两个代码如果有大佬能够优化欢迎指点~
AC代码不放出了。