标签:
很有意思的一道贪心题,自己没想出来,理解了一下别人的思路
参考:http://www.cnblogs.com/yu-chao/archive/2012/02/19/2358565.html
http://my.oschina.net/locusxt/blog/210536
题目大意:给你一颗树,树上每个节点都有自己的权值,现在要把这棵树上的所有节点染上颜色,每染一个节点需要一个单位的时间,染一个点的花费是该点的权值乘以当前时间(时间从1开始)。规定在染当前点之前,必须先染他的父亲节点,求最小的花费。
样例分析
Sample Input
5 1 1 2 1 2 4 1 2 1 3 2 4 3 5
表示这棵树有5个节点,根节点为1。接下来的5个数字代表每个点的权值,接下来的n-1行每行有两个数字v1,v2,代表v1是v2的父亲节点。
思路:刚拿到这道题的时候,我完全没有思路,因为每次染一个点之前必须把他父亲节点先染色,这样的话情况非常复杂。后来看了别人的报告,发现这道题的贪心策略非常特别
首先,每个点分别属于一个集合,每个点集具有的属性是:1、点集中点的个数,下文称为nodeNum[i]。2、点集中所有点权值的和,下文中称为fact[i]。3、现在引入一个新的变量val[i],val[i]=fact[i]/nodeNum[i] 贪心策略就是,每次取val值最小的点进行染色。
现在的问题是取到该点时应该怎么染色,换句话说,应该如何求出答案。
现在以样例进行说明:
(1)首先取得集合5,由于要先染父节点,于是把5并入点集3,这样形成一个新的点集记为A集{3,5},将A集合的属性进行更新
ans+=nodeNum[3]*fact[5]=4 (这样可以保证在5之前染色的点所花费的时间考虑进来了)
A集合属性更新为nodeNum[A]=2,fact[A]=4+1=5 Val[A]=5.0/2;
(2)接下来取得集合A,对于A,先把集合A并入集合1,这样形成一个新的点集B{1,3,5}
ans+=nodeNum[1]*fact[A]=9
B集合属性更新为nodeNum[B]=3,fact[B]=6,val[B]=6/3;
(3)然后取得集合2,同理2并入B后形成新集合C{1,3,5,2}
ans+=nodeNum[B]*fact[2]=15
C集合属性更新为nodeNum[C]=4,fact[C]=8,val[C]=2
(4)取得集合4,4并入C中形成新的集合D{1,3,5,2,4}
ans+=nodeNum[C]*fact[4]=23
nodeNum[D]=5,fact[D]=10
显然,每个节点染色的时间都要比实际时间少了1,所以最后要加回来,即ans+=fact[D]=33
那么这样做为什么是正确的呢?根据参考,简单证明一下:
要证明该结果正确,就是要证明:对于fact[i]/nodeNum[i](以下称为平均权值)最大的点,一旦他的父亲节点染色,他应当立即被染色。
不妨假设平均权值最大的点为a,a的父亲节点为b,如果在b被染色之后,a没有立即被染色,即染色次序为 …b x1 x2 …… xn a …
也就是说,在b被染完色之后,a被染色之前,还有若干节点被染过色了。显然,如果我们能证明a与n互换位置之后的花费比不交换位置少,那么就可以证明a应该在b被染色后立即被染色。假设集合xn的总权值Wxn,点数Dxn,集合a的总权值为Wa,点数为Da。
由于Wa/Da > Wxn / Dxn ,显然,Wxn*Da-Wa*Dxn<0,也就是说,在交换位置后,花费会减少,得证。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int C[2010],vis[2010],ans,dep; int n,r; int cnt,jihe[2010]; struct node { int fa; }nod[2010]; struct Set{ int nodeNum; double val; int fact; }ji[2010]; void Initial() { for(int i=1;i<=n;i++){ ji[i].nodeNum=1; ji[i].val=C[i]; ji[i].fact=C[i]; nod[i].fa=i; } } void update(int u) { if(u==r){ vis[u]=1; return ; } else{ vis[u]=1; int v=nod[u].fa; if(ji[v].nodeNum==1){ cnt++; if(jihe[u]!=0){ for(int i=1;i<=n;i++){ if(i!=u && jihe[i]==jihe[u]){ jihe[i]=cnt; } } } jihe[u]=cnt; jihe[v]=cnt; } else{ if(jihe[u]!=0){ for(int i=1;i<=n;i++){ if(i!=u && jihe[i]==jihe[u]){ jihe[i]=jihe[v]; } } } jihe[u]=jihe[v]; } ans+=ji[u].fact*ji[v].nodeNum; ji[v].nodeNum+=ji[u].nodeNum; ji[v].fact+=ji[u].fact; ji[v].val=1.0*ji[v].fact/(1.0*ji[v].nodeNum); for(int i=1;i<=n;i++){ if(jihe[i]==jihe[v]){ ji[i]=ji[v]; } } //cout<<"u "<<u<<" v "<<v<<" fact "<<ji[v].fact<<" val "<<ji[v].val<<" ans "<<ans<<endl; } } int main() { while(~scanf("%d%d",&n,&r) && (n!=0||r!=0)){ for(int i=1;i<=n;i++){ scanf("%d",&C[i]); } Initial(); for(int i=0;i<n-1;i++){ int v1,v2; scanf("%d%d",&v1,&v2); nod[v2].fa=v1; } memset(vis,0,sizeof(vis)); ans=0; int s; cnt=0; memset(jihe,0,sizeof(jihe)); for(int i=1;i<=n;i++){ int maxi,flag=0; double maxn=0; for(int j=1;j<=n;j++){ if(!vis[j]){ if(ji[j].val>maxn){ maxi=j; maxn=ji[j].val; flag=1; } } } if(!flag){ break; } s=maxi; //cout<<"s "<<s<<endl; update(maxi); } ans+=ji[s].fact; cout<<ans<<endl; } return 0; }
标签:
原文地址:http://www.cnblogs.com/Scale-the-heights/p/4638686.html