#1071 给树染色 题解

题目大意

Acwing | UOJ

题目大意:一棵树,n个节点,每个节点有一个值 \(C_i\),只有在每个点的父亲染色后才能给其染色,染色的代价是 \(C_i * T\)\(T\) 表示每个点第个被染色。计算代价最小值。

解题思路

贪心里的排序问题,考虑两种方案:

  1. 先染值最大的的点ma以及他的父亲f再染另一个点w
  2. 先染w,再染fma

1 的代价:\(C_f*t+C_{ma}*(t+1)+C_w*(t+2)······\ ①\)

2 的代价:\(C_w*t+C_f*(t+1)+C_{ma}*(t+2)······\ ②\)

两式相减 ( ① - ② ):\(2*C_w-C_{ma}-C_f\)

若 ① > ② 即:\(C_w>{\frac{C_{ma}\ +\ C_f}{2}}\)
反之则小于

然后就可以每次合并最大的点和他的父亲,并且改变一下答案就好

可以参考这张图片的解法:

AC 代码

$Code$
#include<bits/stdc++.h>
using namespace std;

const int N=1005;
int n,root;
bool vi[N];
struct node{
	int sum,cnt,fa;
	double ave;
}tr[N];

int read(){
	int sum=0,f=1;char a=getchar();
	while(a<'0' || a>'9'){if(a=='-')	f=-1;a=getchar();}
	while(a>='0' && a<='9')	sum=sum*10+a-'0',a=getchar();
	return sum*f;
}
int fi(){
	double ma=-1.0;
	int ans;
	for(int i=1;i<=n;++i){
		if(i==root)	continue;
		if(!vi[i] && ma<tr[i].ave)
			ma=tr[i].ave,ans=i;
	}
	return ans;
}

int main(){
	
	while(n=read(),root=read()){
		if(!n && !root)	break;
		int ans=0;
		memset(tr,0,sizeof tr);
		memset(vi,0,sizeof vi);
		
		for(int i=1;i<=n;++i)
			tr[i].ave=tr[i].sum=read(),tr[i].cnt=1,ans+=tr[i].sum;
		for(int i=1;i<n;++i){
			int x=read(),y=read();
			tr[y].fa=x;
		}
		
		for(int i=1;i<n;++i){
			int p=fi(),f=tr[p].fa;
//			cout<<p<<" ";

			for(int i=1;i<=n;++i)
				if(tr[i].fa==p)	tr[i].fa=f;

			ans+=tr[p].sum*tr[f].cnt;
			tr[f].cnt+=tr[p].cnt,tr[f].sum+=tr[p].sum;
			tr[f].ave=(double)(1.0*tr[f].sum/tr[f].cnt);
			vi[p]=1;
			
//			cout<<ans<<endl;
		}
		printf("%d\n",ans);
	}
	
	return 0;
}
posted @ 2022-06-26 17:32  _yolanda  阅读(69)  评论(0编辑  收藏  举报