ACM-贪心入门:POJ3253 Fence Repair

ACM-贪心入门:POJ3253 Fence Repair

          • 中文题意
          • 输入
          • 输出
          • 示例输入
          • 示例输出
          • 解决思路
          • AC代码

为简化文章,原题请直接看原题链接

中文题意

给你一块长木板,其长度为L,要求将其切割成若干块指定大小的木板,大小分别为a,b,c,d…
其中L恰好等于a+b+c+d+…,每切割一次,所需的花费为当前执行切割操作的木板的的长度。比如将长度为P的木板切割为K和H,则花费为P。求最小的花费的切割方法的所需花费。

输入

第1行给出整数 N,N代表切割后的木板的个数
第2到N+1行,每行给出1个整数,代表一块切割后的木板的长度

输出

输出一行,该行含有一个整数K,K代表题意所需的结果。

示例输入

3
8
5
8

示例输出

34

解决思路

示例输入输出的结果34是这样子得来的:21切割成13和8,花费21。将长度为13的木板切割为8和5,花费13.一共花费21+13=34。
以“二叉树”的形式,看待每一次切割。
将执行分割的木板长度为A[ i ],分割后的木板的长度分别为 A[ 2*i ] , A[ 2*i+1 ]
则有A[ i ]=A[ 2*i ]+A[ 2*i+1 ]
经过稍微思考,可知所需花费是非叶子结点的权值的总和·
再经过观察,不难发现 所有“叶子结点的权值乘以其路径”的总和为其所需花费
根据以上特征,不难联想到哈夫曼树

  • 将N块木板的长度初始化为N棵树,该树(只有一个结点)的权值即为对应木板的长度。
  • 利用贪心算法,每次选出权值最小的两颗树作为左右子女结点(即木板)生成一颗新树并删除原来的两棵树,新的树的根结点权值为原来两棵树的权值之和。最后剩下的一棵树就是哈夫曼树,该树的“花费“(叶子结点的权值乘以其路径)即为题目所求。

哈夫曼树不了解的同学请自行搜索,了解,这里不再赘述。

AC代码
#include
#include
using namespace std;
typedef long long ll;
//maxn将最大堆变最小堆,请看后续代码 
const ll maxn = 1000000009;
//树 
struct Node{
	ll sum;//构建该树的总耗费 
	ll basic;//叶子结点的权值总值 
};
//构建最大堆之用 
bool operator<(const Node &a,const Node &tmp){
		return a.basic<tmp.basic;
}
//最大堆 
priority_queue<Node> q;
//将叶子结点的权值总值,即树根节点的权值进行类似于“求补码”的操作 
inline void re(Node &tmp){
	//maxn的用处在此 
	tmp.basic=maxn-tmp.basic;
}
int main(){
	ll n,tmp;
	//前两大的树(求补后即前两小) 
	Node work;
	Node work2;
	work.sum = 0;
	scanf("%lld",&n);
	for(ll i = 0;i<n;++i){
		scanf("%lld",&tmp);
		work.basic = tmp;
		//求补,最大变最小 
		re(work);
		q.push(work);
	}
	
	while(q.size()!=1){
		work = q.top();q.pop();
		work2 = q.top();q.pop();
		//求补,还原数据 
		re(work);
		re(work2);
		//更新叶子结点权值总值 
		work.basic+=work2.basic;
		//更新总耗费 
		work.sum+=work2.sum;
		work.sum+=work.basic;
		//求补,重新入优先级队列 
		re(work);
		q.push(work);
	}
	printf("%lld\n",q.top().sum);
	return 0;
}

你可能感兴趣的:(贪心,ACM,ACM,贪心算法,哈夫曼树,最小堆)