POJ3253解题报告

题目的大致意思为:有一块长木板,要经过n-1次切割将其切成n块FJ想要的木板,对于每块木板,没切割一次,将会消耗和这条木板长度值相等的金钱,问最少需要多少钱,可将木板切成自己想要的n块。

算法:

题目看起来像是用DP来解,DP是可以解决,不过再看看数据量,哇塞n<=20000;DP是铁定会超时的。参加国高中信息学竞赛的同学肯定知道这道题和NOIP2004的“合并果子”是非常相似的,只不过合并果子题意较明显,是由下而上,而POJ3253是由上而下。但本质上都一样的,都是构造哈夫曼树——贪心算法。每次取两个木板长度最小的值相加,其和作为花费加到ans上,直到只剩下一个木板为止。

实现方法:

   自然而然会想到排序,用哪种排序好呢?由于每次只用到队列的前两个数,所以插排是个不错的选择,不过仅仅插入排序还是不够的,对于20000的数据量,加上优先队列优化会更好些。

   另一种想法是堆。因为堆的实现和维护都相当简单,且其算法性能稳定,遇到极端数据也不怕。

   共进行n-1次合并,用堆实现的话,每次调整时间复杂度为logN,所以总的时间复杂度为NlogN,下面给出堆实现的源代码:

#include 
using namespace std;
long long n,i,ans,p[20001];
void heap(long x,long y)
{
  long i,j,temp;
  temp=p[x];
  i=x;
  j=i*2;
  while(j<=y)
  {
    if(j<y&&p[j+1]<p[j])
      j++;
    if(temp>p[j])
    {
      p[i]=p[j];
      i=j;
      j=i*2;             
    }       
    else
      break;    
  }     
  p[i]=temp;
}
int main()
{
  cin>>n;
  ans=0;
  for(i=1;i<=n;i++)
    cin>>p[i];
  for(i=n/2;i>=1;i--)
    heap(i,n);
  while(n>1)
  {
    p[0]=p[1];
    p[1]=p[n--];
    heap(1,n);
    p[1]+=p[0];
    ans+=p[1];
    heap(1,n);          
  }  
  cout<<ans<<endl;
  return 0;
}
对于优先队列,我没有去写代码,只是分析了一下,有兴趣的朋友可以自己试一下,感觉用STL实现它还是比较方便的,呵呵。

你可能感兴趣的:(POJ3253解题报告)