#1071 给树染色 题解
题目大意
题目大意:一棵树,
n
个节点,每个节点有一个值 \(C_i\),只有在每个点的父亲染色后才能给其染色,染色的代价是 \(C_i * T\) ,\(T\) 表示每个点第个被染色。计算代价最小值。
解题思路
贪心里的排序问题,考虑两种方案:
- 先染值最大的的点
ma
以及他的父亲f
再染另一个点w
- 先染
w
,再染f
和ma
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;
}