题目链接

代码借鉴此博:http://www.cnblogs.com/vongang/archive/2011/08/19/2146070.html

其中关于max{c[fa]/t[fa]}贪心原则,此博有很好的解释:http://www.cnblogs.com/rainydays/p/3271277.html

在此引用其中几段话:

试想,如果没有父节点排在节点之前的限制,那么这个题目非常简单,只需要将结点按照权值从大到小排列即可。加上了这个限制之后,如果权值最大的那个节点一旦满足了条件(父节点被排在了之前的某个位置),那么这个权值最大的节点一定要紧挨着这个父节点,即把这个权值最大的节点排在它所能排的最前面的位置。因为对于这个节点如果不受限制应该排在第一位,而有了限制,在满足了限制之后也应把它尽可能地排在前面。所以它一定是挨着父节点的。那么现在在最终的排列中我们确定了两个节点的前后相邻关系,将他们绑定在了一起。

试想如果保持这个相邻关系的同时去掉其他节点的限制,那么我们应该如何排列呢?我们假设绑定在一起的两节点是a和b。现有一个另外的节点x,我们看两种排列xab,abx对最终的计算结果有什么影响。x*i+a*(i+1)+b*(i+2); a*i + b*(i+1) + x*(i+2)。后者减去前者等于2x-(a+b)。即将x从ab之前挪到ab之后,ab各左移1位,结果减小a+b。x右移2位结果增加2x。因此两者谁在前谁在后我们只需要比较a+b和2x即可,也可以比较(a+b)/2和x。

将这个定理进行一下推广,绑定在一起的不一定是两个节点,可以是一个更长的序列,与这个序列进行比较看谁放在前面的也可以是一个序列。设一个序列有n1个节点,第二个序列有n2个节点。那么我们比较两者谁放在前面的时候需要比较的是(n1个权值之和×n2)和(n2个权值之和×n1)。即左移和右移产生的结果变化。当然也可以比较(n1个权值之和/n1)和(n2个权值之和/n2)。

我们可以再次进行推广,如果我们要排列的不是节点,而是许多序列的话,那么我们只需要计算每个序列权值的平均数(例如:n个节点的序列,要计算n个权值之和/n),然后按照这个平均数从大到小排列即可使得计算结果最小。这样就可以让序列与节点有了一个统一的衡量值——平均数。

这样一来,我们就可以将上面的绑定两节点的操作看成是将问题规模缩小的操作,在帮定两节点的同时我们在树中也将两节点合并,变为一个节点,即将子节点的孩子变为父节点的孩子。然后合并后的节点的权值是合并在这个节点中的所有节点的权值的平均数。我们成功的将问题规模减小了1。只需要不断这样做即可将问题缩减为只有一个节点。


以下为AC code:

//贪心:如果这棵树中有最大权值点X(非根),
//那么一旦X的父节点Y已经染色,就应该立刻染X
//于是X和Y合并成一个点集,
//新点集的权值=(新点集中所有点的权值和)/(新点集中点的个数)
//类似的,该点集可以看为一个点
//重复上述贪心思路,直到最后只剩下一个根r点集
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std; const int N=1005; struct Node
{
int f;//父结点
int t;//时间
int c;//原权值
double w;//贪心权值(c/t)
}node[N]; int n,r; int findPos()
{
int pos;
double wmax=0;
for(int i=1;i<=n;i++)
if(node[i].w>wmax&&i!=r)
{
wmax=node[i].w;
pos=i;
}
return pos;
} int main()
{
while(scanf("%d%d",&n,&r)&&n+r)
{
int pos,fa,res=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&node[i].c);
node[i].w=node[i].c;//将贪心权值w初始化为原权值c
node[i].t=1;//时间初始化为1
res+=node[i].c;//结果初始化为sum of所有原权值c
}
int u,v;
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
node[v].f=u;//记录父结点
} for(int i=1;i<n;i++)
{
pos=findPos();//找到贪心权值最大的位置
node[pos].w=0;//置0,以便下次查找时跳过之
fa=node[pos].f;//fa为pos的父结点
res+=node[pos].c*node[fa].t;//res+=pos原权值*fa父结点时间(node[fa].t实际上代表,点集中有几个点)
for(int j=1;j<=n;j++)
if(node[j].f==pos)
node[j].f=fa;//若有j的父结点是pos,则将j的父结点改为fa,建立新集合
node[fa].t+=node[pos].t;//更新以fa为父结点的点集内点的个数(即加上以pos为父结点的点集内点的个数)
node[fa].c+=node[pos].c;//更新点集的话,那权值自然也要更新
node[fa].w=(double)node[fa].c/node[fa].t;//贪心原则:新点集权值和/新点集中点的个数
}
printf("%d\n",res);
}
return 0;
}

POJ 2054 Color a Tree#贪心(难,好题)的更多相关文章

  1. POJ 2054 Color a Tree (贪心)

    $ POJ~2054~Color~a~Tree $ $ solution: $ 我们先从题中抽取信息,因为每个点的费用和染色的次数有关,所以我们可以很自然的想到先给权值大的节点染色.但是题目还说每个节 ...

  2. POJ 2054 Color a Tree解题报告

    题干 Bob is very interested in the data structure of a tree. A tree is a directed graph in which a spe ...

  3. POJ 2054 Color a Tree

    贪心....                    Color a Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions:  ...

  4. poj 2054 Color a Tree(贪婪)

    # include <stdio.h> # include <algorithm> # include <string.h> using namespace std ...

  5. $Poj2054\ Color\ a\ Tree\ $ 贪心

    $poj$ $Description$ 一颗树有 $n$ 个节点,这些节点被标号为:$1,2,3…n,$每个节点 $i$ 都有一个权值 $A[i]$. 现在要把这棵树的节点全部染色,染色的规则是: 根 ...

  6. 【POJ 2054】 Color a Tree

    [题目链接] http://poj.org/problem?id=2054 [算法] 贪心 [代码] #include <algorithm> #include <bitset> ...

  7. poj2054 Color a Tree

    神题.这题是巨毒瘤... 自己写真可谓是: 排空驭气奔如电,上天入地求之遍 上穷碧落下黄泉,两处茫茫皆不见 由于我们知道:不是树形时,不停选值最大的节点可以得到最小代价. 那么我们就能想出一个错误的贪 ...

  8. Color a Tree & 排列

    Color a Tree 题目链接 好不可做?可以尝试一下DP贪心网络流.DP 似乎没法做,网络流也不太行,所以试一下贪心. 考虑全局中最大权值的那个点,如果它没父亲,那么一定会先选它:否则,选完它父 ...

  9. Color a Tree[HDU1055]

    Color a Tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tota ...

随机推荐

  1. Fatal error: Call to undefined function oci_connect()

    http://stackoverflow.com/questions/22478387/call-to-undefined-function-oci-connect Whenever you conn ...

  2. MVC-Area

    ASP.NET MVC中,是依靠某些文件夹以及类的固定命名规则去组织model实体层,views视图层和控制层的.如果是大规模的应用程序,经常会由不同功能的模块组成,而每个功能模块都由MVC中的三层所 ...

  3. webservices(一)

    ---完全摘自网络 什么是webService WebService,顾名思义就是基于Web的服务.它使用Web(HTTP)方式,接收和响应外部系统的某种请求.从而实现远程调用.  1:从WebSer ...

  4. Tiny6410之NAND FLASH驱动

    一.NAND FLASH的特点 S3C6410的NAND FLASH控制器有如下特点 1.自导入模式:复位后,引导代码被送入到8KB的STEPPINGSTONE中,引导代码移动完毕,引导代码将在STE ...

  5. 【原创】RMQ - ST算法详解

    ST算法: ID数组下标: 1   2   3   4   5   6   7   8   9    ID数组元素: 5   7   3   1   4   8   2   9   8 1.ST算法作 ...

  6. linux upstart启动配置

    程序名.conf放在/etcc/init/目录下# 注释 description "your-server" author "xxx" start on run ...

  7. 无法定位程序输入点__gxx_personality_v0的一个解决方法

    windows系统,使用mingw32-g++编译一个简单的工程,编译链接过程都没有错误提示,但是运行的时候会弹出提示框提示"无法定位程序输入点__gxx_personality_v0&qu ...

  8. Oracle新实例创建

    http://blog.itpub.net/29519108/viewspace-1443918/ 刚开始创建时,千万别点容器数据库,不然后面新建用户时,用户名前得加C##. 常用命令: sqlplu ...

  9. 【卷二】网络二—TCP服务器与客户端

    经过上回简单地介绍,大家对服务器多少应该清楚一些了吧!还记得TCP: (Transmission Control Protocol) 传输控制协议? 还记得IP: (Internet Protocol ...

  10. MediaPlayer的错误列表速查(android)

    public static final int MEDIA_ERROR_IO Added in API level 17 File or network related operation error ...