题目链接: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;
}