Acwing115 给树染色(贪心)

一颗树有 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;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值