合并果子

合并果子

 

Time Limit:   1000MS       Memory Limit:   65535KB
Submissions:   4092       Accepted:   466

 

Description

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。 例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

 

Input

输入包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。

 

Output

输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。

 

Sample Input

3 
1 2 9

 

Sample Output

15

 

Hint

对于30%的数据,保证有n<=1000: 对于50%的数据,保证有n<=5000; 对于全部的数据,保证有n<=10000

 

分析:

大致题意:就是每次将数目最少的两种果子加起来形成新的一种,并同时累计每次的和

思路分析:

【解法一】

并不是每次所得和的值都是最小的,所以有以下想法:①加一次排一次序,时间复杂度为O(n^3)②那排序花的时间多,就优化排序吧,其实只是一个数的变动,用插入吧,在多想一点折半查找,时间复杂度也是O(n^2*logn),显然还是行不通

这里想到用hash就对了,输入一个数num,就sign[num]++,每次从1开始往后取,就减少了排序过程;再者,由于一开始所有数据,都在20000内,那就定sign[40000]吧,为什么呢?后面的20000是加的次数,如果也将和sum作sign[sum]++处理的话,以题上的数据量,和最大可以达到28000000,开这么大的数组肯定不可能,那这里有个很不错的方法:就是将数组分开用,前20000---sign[num]++,超过20000的---sign[pos++]=sum(pos初始值为20001)。

就看代码吧

# include<stdio.h>
# include<string.h>
#define N 20000
int now;
int sign[2*N];
int solve(int sign[])
{
    while(sign[now]==0)
        now++;
    if(now<=N)
    {
        sign[now]--;//用了一个就减去一个
        return now;
    }
    else return sign[now++];//如果now找到了20000以上,返回sign[now]
}
int main()
{
   int n,i;
   int num,s1,s2,sum=0;
   int pos=N+1;
   scanf("%d",&n);
   memset(sign,0,sizeof(sign));
   for(i=0;i<n;i++)
   {
   scanf("%d",&num);
   sign[num]++;
   }
   now=1;
   for(i=0;i<n-1;i++)
   {
       s1=solve(sign);
       s2=solve(sign);
       sum+=s1+s2;
       if(s1+s2<=N)
       {
           sign[s1+s2]++;
       }
       else 
       {
           sign[pos++]=s1+s2;//如果now找到了20000以上,就换成这种方法来存
       }
   }
   printf("%d\n",sum);
   return 0;
}

 【解法二】

其实就是贪心算法,但还是上面说的,没有合适的调整算法,肯定会超时;这里想到一种,虽然没第一种方法节省时间,但是很明显的贪心,就是用优先队列

代码如下:

# include<iostream>
# include<queue>
# include<algorithm>
using namespace std;
int main()
{ 
    int sign[10005];
    int n,temp,sum=0;
    int i;
 priority_queue<int,vector<int>,greater<int> > q;
  cin>>n;
  for(i=0;i<n;i++)
  {
   cin>>sign[i];
   q.push(sign[i]);
  }
  while(!q.empty())
  {
   temp=q.top();
   q.pop();
   if(!q.empty())
   {
    temp+=q.top();
    q.pop();
   }
   sum+=temp;
   if(q.empty())
    break;
   q.push(temp);
  }
  cout<<sum<<endl;
  return 0;
}

注:这道题是求最小消耗值,所以最小两个加起来不一定是最小的,需要再调整顺序;但如果是求最大的消耗值的话,最大两个加起来一定是最大的,就可用以下代码,时间复杂度为O(n)

 

# include<stdio.h>  
# include<algorithm>
using namespace std;
int main()
{
 int sign[10005];
 int n,sum=0;
 int i;
 scanf("%d",&n);
 for(i=0;i<n;i++)
  scanf("%d",&sign[i]);
 sort(sign,sign+n);
 for(i=n-1;i>=0;i--)
  sum+=(i+1)*sign[i];
 if(n>=1)
  sum-=sign[n-1];
 printf("%d\n",sum);
 return 0;
}

 

 

 

posted on 2012-11-11 21:36  即为将军  阅读(375)  评论(0)    收藏  举报

导航