题目的大致意思为:有一块长木板,要经过n-1次切割将其切成n块FJ想要的木板,对于每块木板,没切割一次,将会消耗和这条木板长度值相等的金钱,问最少需要多少钱,可将木板切成自己想要的n块。
算法:
题目看起来像是用DP来解,DP是可以解决,不过再看看数据量,哇塞n<=20000;DP是铁定会超时的。参加国高中信息学竞赛的同学肯定知道这道题和NOIP2004的“合并果子”是非常相似的,只不过合并果子题意较明显,是由下而上,而POJ3253是由上而下。但本质上都一样的,都是构造哈夫曼树——贪心算法。每次取两个木板长度最小的值相加,其和作为花费加到ans上,直到只剩下一个木板为止。
实现方法:
自然而然会想到排序,用哪种排序好呢?由于每次只用到队列的前两个数,所以插排是个不错的选择,不过仅仅插入排序还是不够的,对于20000的数据量,加上优先队列优化会更好些。
另一种想法是堆。因为堆的实现和维护都相当简单,且其算法性能稳定,遇到极端数据也不怕。
共进行n-1次合并,用堆实现的话,每次调整时间复杂度为logN,所以总的时间复杂度为NlogN,下面给出堆实现的源代码:
#include <iostream>
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实现它还是比较方便的,呵呵。