一颗树有 n 个节点,这些节点被标号为:1,2,3…n,每个节点 i 都有一个权值 A[i]。
现在要把这棵树的节点全部染色,染色的规则是:
根节点R可以随时被染色;对于其他节点,在被染色之前它的父亲节点必须已经染上了色。
每次染色的代价为T*A[i],其中T代表当前是第几次染色。
求把这棵树染色的最小总代价。
输入格式
第一行包含两个整数 n 和 R ,分别代表树的节点数以及根节点的序号。
第二行包含 n 个整数,代表所有节点的权值,第 i 个数即为第 i 个节点的权值 A[i]。
接下来n-1行,每行包含两个整数 a 和 b ,代表两个节点的序号,两节点满足关系: a 节点是 b 节点的父节点。
除根节点外的其他 n-1 个节点的父节点和它们本身会在这 n-1 行中表示出来。
同一行内的数用空格隔开。
输出格式
输出一个整数,代表把这棵树染色的最小总代价。
数据范围
1≤n≤1000 1≤n≤1000,
1≤A[i]≤1000 1≤A[i]≤1000
输入样例:
5 1
1 2 1 2 4
1 2
1 3
2 4
3 5
输出样例:
33
最优解法:每次取点权平均值最大的点,合并到他的父节点上,并更新父节点的平均值和点数(注意每个点初始为数目为1,初始平均值为本身点权)。
证明: 假如有权值为 x,y,z的三个点,已知x,y的染色操作是连续的,那么就有2种染色可能:
1.先染x,y,后染色 z,代价为: x+2y+3z
2.先染 z,再染 x,y,代价是 z + 2x + 3y
若方案一优于方案二,则 z > (x+y)/2,所以优先取平均值最大的点集。
另外每个子点集与父点集合并时,子点集的ans会往后偏移 父点集的点数*子点集的权值(这种偏移的思想比较重要),还有就是平均值要用double型,每次找到均值最大的点与父点合并后,均值要更新(最好更新为负数,否则每次都取这个点)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct node{
double ave;
int fa;
int s;
int num;
}p[1005];
int n,r;
int find() {
double ave = -1,pos;
for(int i=1;i<=n;i++) {
if(i!=r&&p[i].ave>ave) {
ave = p[i].ave;
pos = i;
}
}
return pos;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> r;
ll ans = 0;
for(int i=1;i<=n;i++) {
cin >> p[i].s;
ans += p[i].s;
p[i].num = 1;
p[i].ave = p[i].s;
}
int a,b;
for(int i=0;i<n-1;i++) {
cin >> a >> b;
p[b].fa = a;
}
for(int i=0;i<n-1;i++) {
int now = find();
//cout << now << endl;
int father = p[now].fa;
ans += p[now].s*p[father].num;//子点集向后偏移
p[father].num += p[now].num;//父点更新
p[father].s += p[now].s;
for(int j=1;j<=n;j++) {
if(p[j].fa == now) {
p[j].fa = father;//子点的儿子的父亲直接更新为祖父,以便于后面偏移
}
}
p[now].ave = -1;//找到的点均值更新
p[father].ave = (double)(1.0*p[father].s/p[father].num);
}
cout << ans;
}