Solution
众所周知这是一道dl贪心
Step 1
如果没有先后染色的顺序让你去染色,你肯定会先把最大的节点先染了,但是如果有了限制,类似的,就会把子节点中最大的点先染了,所以两个操作会是连续的。
所以假设待染色的点分别为 x,y,z ,其中 x,y 已知是连续染色,则只有两种染法
1.x+2y+3z1.x+2y+3z 先染 x,y ,再染 z
2.z+2x+3y2.z+2x+3y 先染 z ,再染 x,y
比较两数大小, 1-2=-x-y+2z
也就是比较
(x+y)/2
也就启发我们运用平均值作为性价比,每次扫描一遍求出最大性价比进行染色,将这一个点放在父亲节点中最后一个点之后马上染色,直到最后将整棵树缩成一个点,得到最终答案
Step 2
#include<bits/stdc++.h> using namespace std; int n,r,ans; struct node{int c,fa,t;double w;}num[1005]; int find(){//求出最大性价比 int mbi; double maxn=0; for(int i=1;i<=n;i++) if(i!=r&&num[i].w>maxn) maxn=num[i].w,mbi=i; return mbi; } int main(){ while(scanf("%d %d",&n,&r)&&n&&r){ ans=0; for(int i=1;i<=n;i++)scanf("%d",&num[i].c),ans+=num[i].c,num[i].t=1,num[i].fa=0,num[i].w=num[i].c;//预处理清空 for(int i=1,a,b;i<n;i++)scanf("%d %d",&a,&b),num[b].fa=a; for(int i=1;i<n;i++){ int tmp=find(); num[tmp].w=0; int f=num[tmp].fa; ans+=num[tmp].c*num[f].t;//合并性价比最大的节点,在父亲的节点之后紧跟着被染色 for(int j=1;j<=n;j++) if(num[j].fa==tmp) num[j].fa=f;//合并 num[f].t+=num[tmp].t;//更新父亲节点 num[f].c+=num[tmp].c; num[f].w=(double)(num[f].c)/num[f].t; } printf("%d ",ans);//输出 } }