HDU 1055 ---给树着色
Color a Tree
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 983 Accepted Submission(s):
319
Bob intends to color all the nodes of a tree with a pen. A tree has N nodes, these nodes are numbered 1, 2, ..., N. Suppose coloring a node takes 1 unit of time, and after finishing coloring one node, he is allowed to color another. Additionally, he is allowed to color a node only when its father node has been colored. Obviously, Bob is only allowed to color the root in the first try.
Each node has a “coloring cost factor”, Ci. The coloring cost of each node depends both on Ci and the time at which Bob finishes the coloring of this node. At the beginning, the time is set to 0. If the finishing time of coloring node i is Fi, then the coloring cost of node i is Ci * Fi.
For example, a tree with five nodes is shown in Figure-1. The coloring cost factors of each node are 1, 2, 1, 2 and 4. Bob can color the tree in the order 1, 3, 5, 2, 4, with the minimum total coloring cost of 33.

Given a tree and the coloring cost factor of each node, please help Bob to find the minimum possible total coloring cost for coloring all the nodes.
A test case of N = 0 and R = 0 indicates the end of input, and should not be processed.
#include<iostream>
#include<algorithm>
using namespace std;
typedef struct node
{
int num;
int father;
int cost;
bool visit;
}Node;
Node Nd[1002];
int total,tim;//total记录总花费;tim记录时间
void UnionSet(int a,int b) //构成有向图
{
Nd[b].father=a;
}
void proce(int i)
{
if(Nd[i].visit) return ;
if(Nd[i].num==Nd[i].father) //如果当前节点是根节点
{
total+=Nd[i].cost*tim; //对该节点着色,所有花费的总和
tim++; //时间+1
Nd[i].visit=true; //标记该节点已经着色
return ;
}
else //如果该节点没有被处理,且不是根节点
{
proce(Nd[i].father);//先处理其父节点
total+=Nd[i].cost*tim;
tim++;
Nd[i].visit=true;
}
}
bool cmp(Node x,Node y)
{
return x.cost>y.cost;
}
int main()
{
int n,m;
while((cin>>n>>m)&& n && m )
{
for(int i=1;i<=n;i++) //节点初始化
{
cin>>Nd[i].cost;
Nd[i].father=i;
Nd[i].num=i; //编号
Nd[i].visit=false;
}
int x,y;
for(int i=1;i<n;i++) //构造树,
{
cin>>x>>y;
UnionSet(x,y);
}
sort(Nd+1,Nd+n+1,cmp);
for(int i=1;i<=n;i++)
cout<<" ( "<<Nd[i].cost<<" , "<<Nd[i].num<<" , "<<Nd[i].father<<" , "<<Nd[i].visit<<" ) "<<endl;
total=0;tim=1;
cin>>total;
for(int i=1;i<=n;i++)
{
proce(i);
}
cout<<total<<endl;
}
return 0;
}
这题跟HDU 3979(打怪兽有联系,又有区别)
联系:都是使用贪心算法,按某一值排序后(降序),先处理最大值,直到结束
区别:这里是一棵树,节点与节点之间有联系(父亲---儿子),但是怪兽间是没有关系的;
起初看到树,就想到之前的并查集,但是后来发现行不通,原因是,使用并查集好像不能随便改变元素间的位置吧,不然怎么知道之前它的位置在哪里呢?这样就会导致之前记录的关系没有用了,纠结中~~~
看了别人的解题报告,终于弄懂了点:
首先: 使用了并查集的思想,(注只利用了“并”思想);
其次: 没有想象中的那么简单(只考虑val的值)
正解:time-----记录完全解决该节点需要花费的时间
val-------该节点所对应的权值
cost------解决该节点所需要的总花费
所以需要比较的是 a[idx].val*a[i].time<a[i].val*a[idx].time
在将当前节点与父节点合并时,注意点有:
(1)更新的 cost = 父亲的cost + 儿子的cost + 儿子的val * 父亲的time(最后一项是由于要先处理父亲才能处理儿子,所以儿子必须等待“父亲的time”这么多时间,这段时间产生的cost即为 “儿子的val * 父亲的time” );
(2)更新的 time = 父亲的time + 儿子的time
(3) 更新的val = 父亲的val + 儿子的val
(4) 将儿子的节点清空,即这个节点已经不用再次处理了,
在这里,刚开始有疑惑,我就想,如果这样的话,那这个节点被他的父亲包括了,那他的儿子怎么办呢?
后来想了下,明白了,如果这样的话,当这个节点被选中,而他的父亲的代价是0,同时,再一次将这个节点合并到他的父亲节点中,导致父亲节点被更新(非0了),而这个节点被清空,(相当于把这个节点向前移,自己觉得有点浪费时间,但是思路清晰简单)
总结:本人觉得在比较那里有值得学习的地方:
(1)
int idx=0;
for(int i=1;i<=n;i++)
{
if((i!=r)&&(a[i].time)&&((idx==0)||(a[idx].val*a[i].time<a[i].val*a[idx].time)))
idx=i;
}
这里很经典,首先对idx赋初值0;如果非根节点,且未被访问过,并且这是第一个(如果只有一个元素,就无法比较-----很好的思路)或者(不是第一个,就必须进行比较,找到满足要求的)满足比较条件;
(2)
就是将子节点与父节点合并的思路,感觉有点像DP,但又感觉不是太像;还是贪心的思想比较明确,每次寻找的是权值最大的(这里还跟时间有关,注意!)
正确的代码如下:
#include<iostream>
#include<stdio.h>
using namespace std;
int father[1002];
typedef struct node
{
int time;
int val;
int cost;
void clear()
{
time=val=cost=0;
}
}Node;
Node a[1002];
int main()
{
int n,r;
while((cin>>n>>r)&&n&&r)
{
for(int i=1;i<=n;i++)
{
cin>>a[i].val;
a[i].cost=a[i].val;
a[i].time=1;
}
int x,y;
for(int i=1;i<n;i++)
{
cin>>x>>y;
father[y]=x;
}
while(true)
{
int idx=0;
for(int i=1;i<=n;i++)
{
if((i!=r)&&(a[i].time)&&((idx==0)||(a[idx].val*a[i].time<a[i].val*a[idx].time)))
idx=i;
}
if(idx==0) break;
int f=father[idx];
a[f].cost+=a[idx].cost+a[idx].val*a[f].time;
a[f].val+=a[idx].val;
a[f].time+=a[idx].time; //将当前节点和它的父节点合并
a[idx].clear(); //清空
}
cout<<a[r].cost<<endl;
}
return 0;
}
浙公网安备 33010602011771号