合并果子普通版
合并果子加强版
因为需要耗费的体力最小,所以需要每次合并最小的两堆果子.这时,就需要我们开始进行排序,找出最小的两堆果子,进行合并,累加体力.最后,输出体力之和.
首先,第一个想到的肯定是用sort进行排序.但是,会发现 n < = 10000 n<=10000 n<=10000sort的速度肯定过不了(每一次只需要找到一个数的排序位置即可,即只有一个数是无序的.故不需要每一次都排一次序.
这时,就应该想到可以用C++ STL
中的priority_queue
来解决.
每一次弹出最小的两个数,加起来,再弹回去,就可以实现了.
Code:
#include
#include
#include
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;
}
但是,这真的是最快的算法吗???
首先,发现数据范围:
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
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;
}