POJ 2054---Color a Tree

题目链接:poj2054

很好的一道贪心题,是边计算边贪心,此外,理解思路是难点一,实现是难点二,可以说思路和实现都很需清晰的头脑。

#include <iostream>
using namespace std;

/***
好题!discuz上有N=1000的数据,
思路是一下两点:
	1.在未访问的node中,权重最大的点应该在访问它的父亲后马上访问,依此进行点的合并;
	2.合并后点的权重相当于其所包含原始点的权重平均值,推导:w(DABC)-w(ABCD)=3w(D)-w(A)-w(B)-w(C),

实现:
	(求最终费用也很取巧)
	1.这里采用的是假合并,找出当前value最大点cur后,求其真父亲fa,即若父亲已经被合并,求父亲的父亲。
	这样能够知道当访问完fa后,还要访问几个点(.num)才能轮到当前点,于是对sum贡献weight*sum;
	并更新fa的 fa.num += cur.num。

	2.接着将weight加到fa上去,因为对fa的访问每多一单位时间,其对sum的贡献就增加fa.weight

	3.最后的fa显然就是root,由于其访问需要一单位时间,因此sum+=root.weight
	
	copyleft mandycool
***/

namespace p2054{
	class Node{
	public:
		int weight;  //累积的权重
		int fa; //father  
		int num;  //累积到该点的后继节点数
		double value;  //该点的价值
	};

	const int M = 1001;
	Node nodes[M];

};

using namespace p2054;

int main(void){
	int N,root;
	while(1){
		cin>>N>>root;
		if(N==0)
			break;

		for(int i=0;i<N;i++){
			cin>>nodes[i+1].weight;
			nodes[i+1].value = (double)nodes[i+1].weight;
		}
		for(int i=0;i<N-1;i++){
			int v1,v2;
			cin>>v1>>v2;		
			nodes[v1].num = 1;
			nodes[v2].num = 1;
			nodes[v2].fa = v1;
		}
		int nn = N-1;
		int sum = 0;
		while(nn--){
			double max = 0;
			int idx;
			for(int i=1;i<=N;i++){
				if(i==root)
					continue;
				if( nodes[i].value > max){
					max = nodes[i].value;
					idx = i;
				}
			}
			int fa = nodes[idx].fa;
			while(nodes[fa].num==0) // find the true father, which will be root eventully
				fa = nodes[fa].fa;
			
			sum += nodes[idx].weight * nodes[fa].num;
			//update nodes[fa]
			nodes[fa].num += nodes[idx].num;
			nodes[fa].weight += nodes[idx].weight;
			nodes[fa].value = (double)nodes[fa].weight / nodes[fa].num;
			//disable nodes[idx]
			nodes[idx].value = 0;
			nodes[idx].num = 0;

		}
		sum += nodes[root].weight;
		cout<<sum<<endl;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值