合并果子加强版

作者在 2010-07-05 17:09:48 发布以下内容

合并果子加强版

问题描述:n堆果子,你可以选择最多其中m堆果子合并成一堆,合并的代价是这些果子数量之和,求最后合并成一堆果子的最少总代价。

文件输入:n,m(1<=m<=n<=10000)

      以下n行为每堆果子数量(<=10000)

文件输出:一个数为最少总代价;

样例输入:

5 3

3 2 1 4 5

样例输出:21

 

分析:

  该题要注意的是,若每次不能合并M堆,则应该放在第一次合并时,因为当每次合并M堆到最后不够M堆时,还是需要再合并一次,导致总代价增加。而第一次选一合适的堆合并,以后每次都合并M堆,这样总代价最小

解法1

  堆排序

#include<iostream>

using namespace std;

int a[10001],ans=0;

void HeapDown(int root,int n)

{   int k;

    while(root+root<=n)

    {k=root+root;

     if(k<n&&a[k]>a[k+1])k++;

     if(a[k]<a[root])

     {

      swap(a[root],a[k]);

      root=k;

     }

     else return;

    }

}

void HeapDelete(int k)

{  swap(a[k],a[a[0]]);

   a[0]--;

   HeapDown(k,a[0]);//这里k固定为1,只需向下堆化

}

void HeapBuild(void)

{   for(int i=a[0]/2;i>=1;i--)

    HeapDown(i,a[0]);

}  

void Combine(int k)//合并K

{   if(k==0)return;

    int newdata=0,i;

     for(i=1;i<k;i++)//选堆顶累加,并删除堆顶,进行k-1

    {newdata+=a[1];

     HeapDelete(1);

     }

     newdata+=a[1];//再选堆顶累加,并将累加数替换堆顶,向下堆化。

     ans+=newdata;

     a[1]=newdata;

     HeapDown(1,a[0]);

}

int main()

{ 

    int i,k;

    scanf("%d%d",&a[0],&k);

    for(i=1;i<=a[0];i++)

    scanf("%d",&a[i]);

    HeapBuild();

    if((a[0]-1)%(k-1)!=0)

    Combine((a[0]-1)%(k-1)+1);//计算第一次需要合并多少堆

     while(a[0]>1)  Combine(k);

     cout<<ans;

}

 

解法2hash,由于每个数的范围较小,则可用计数排序

 

 

#include<iostream>

using namespace std;

const int oo=0x7fffffff;

int n,m,ans=0,a[10005],b[10005]={0},p1=1,p2=1,cnt[200005]={0},max=20001;

bool flag=1;

/* p1初始指向a的第一个数

p2初始指向b的第一个数,b用来依次存放合并后产生的新数,这些数一定是升序的,b[0]表示b中有多少数*/

void Count_sort()//计数排序

{

  for(int i = 1; i <= n; i++) cnt[ a[i]]++;

  int  z = 1;

  for(int i =0; i <= max; i++)

    for(int j = 0; j < cnt[ i ]; j++) a[z++] = i;

}

void Combine(int t)

{

     int Min=0;

     while( t--)

     {

           if( p1<=n)//a数组有数,从a,b两个数组里找最小

           {

               if( a[p1]>b[p2]&&p2<=b[0])Min+=b[p2++];//最小的在b里面

               else Min+=a[p1++];//最小的数在a里面

               }

           else if(p2<=b[0])Min+=b[p2++];//只有b里面有数,直接在b中取

           else {flag=0;break;}//ab都没有数了

           }

     if(!flag)return;

     b[++b[0]]=Min;ans+=Min;//将新数min放在b最后面

}

int main()

{

    int i,t,p;

    cin>>n>>m;

    for( i=1;i<=n;i++)scanf("%d",&a[i]);

    Count_sort();

    if((n-1)%(m-1)!=0)Combine( (n-1)%(m-1)+1 );

    while( flag )Combine(m);

    cout<<ans;

     return 0;

}

编程 | 阅读 818 次
文章评论,共1条
输侵直创(游客)
2021-11-21 17:12
1
刚刚
游客请输入验证码
文章分类
文章归档
最新评论