合并果子的研究

合并果子的研究


一. 题目概述:

合并果子普通版
合并果子加强版


二. 解体思路:

因为需要耗费的体力最小,所以需要每次合并最小的两堆果子.这时,就需要我们开始进行排序,找出最小的两堆果子,进行合并,累加体力.最后,输出体力之和.

1. 普通版的题解.

首先,第一个想到的肯定是用sort进行排序.但是,会发现 n < = 10000 n<=10000 n<=10000sort的速度肯定过不了(每一次只需要找到一个数的排序位置即可,即只有一个数是无序的.故不需要每一次都排一次序.

这时,就应该想到可以用C++ STL中的priority_queue来解决.
每一次弹出最小的两个数,加起来,再弹回去,就可以实现了.
Code:

#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
priority_queue <int> que;
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=0,x;i<n;++i)
	{
		scanf("%d",&x);
		que.push(-x);
	}
	int ans=0;
	for (int i=1,tmp;i<n;++i)
	{
		tmp=que.top();
		ans-=que.top();
		que.pop();
		tmp+=que.top();
		ans-=que.top();
		que.pop();
		que.push(tmp);
	}
	cout<<ans<<endl;
	return 0;
}

但是,这真的是最快的算法吗???

2. 加强版的题解.

首先,发现数据范围:
n < = 1 0 7 , a i < = 1 0 5 n<=10^7,a_i<=10^5 n<=107,ai<=105
所以,时间复杂度应该为 O ( n ) O(n) O(n)
所以,排序的复杂度必须为 O ( n ) O(n) O(n)
这时,我们发现,只有桶排序才能实现这样的复杂度,所以出现了如下的代码:

void read(int &x)//因为cin的时间复杂度太大,scanf也很大,所以需要用快读.
{
    int f=1;
    x=0;
    char s=getchar();
    while(s<'0' || s>'9')
    {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0' && s<='9')
    {
        x=x*10+s-'0';
        s=getchar();
    }    
    x*=f;
}
int main()
{
	for(int i=1;i<=num;i++)
    {
        read(xx);
        t[xx]++;
    }
}

然后,进行离散化,将空的桶全部"挤出去":

for(int i=1;i<=100001;i++)
    while(t[i])
    {
        t[i]--;
        a1[++n1]=i;
    }

接下来,如果每一次都排一次序,那么时间复杂度又会"攀升".所以,排好序后,就可以不用排序了.而我们会发现,每一次合并耗费的体力都是单调上升的,所以可以用另外一个数组来储存我们合并果子的结果,然后从两个数组中反复找出最小的两个数,储存进第二个数组,知道第一个数组所被指向的指针到头了,就可以停止搜索,输出答案.
代码如下:

#include<bits/stdc++.h>
using namespace std;
int k=1;
int xx;
int num;
int n1,n2;
int t[100000+1];
long long a1[20000000+1],a2[10000000+1];
long long w,sum=0;
void read(int &x)
{
    int f=1;
    x=0;
    char s=getchar();
    while(s<'0' || s>'9')
    {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0' && s<='9')
    {
        x=x*10+s-'0';
        s=getchar();
    }    
    x*=f;
}
int main()
{
    cin>>num;
    memset(a1,0x3f,sizeof(a1));
    memset(a2,127/3,sizeof(a2));
    for(int i=1;i<=num;i++)
    {
        read(xx);
        t[xx]++;
    }
    for(int i=1;i<=100001;i++)
        while(t[i])
        {
            t[i]--;
            a1[++n1]=i;
        }
    int i=1,j=1;
    k=1;
    while(k<num)
    {
        if(a1[i]<a2[j])
        {
            w=a1[i];
            i++;
        }
        else
        {
            w=a2[j];
            j++;
        }
        if(a1[i]<a2[j])
        {
            w+=a1[i];
            i++;
        }
        else
        {
            w+=a2[j];
            j++;
        }
        a2[++n2]=w;
        sum+=w;
        k++;
    }
    printf("%lld",sum);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值