poj 2054 贪心

一道较难的贪心题,看了看大牛的博客才渐渐有了思路:

贪心原则应该是Ci大的尽量先染色,但是由于父节点染了才能染子节点的限制使得问题不好解决了,但是Ci大的一定是在其父节点染色后立即被染色,这时大牛们的思路我也没有看明白如何证明的,但仔细一想就明白了。于是我们根据这个条件就可以将Ci大的点与其父节点合并在一起组成一个集合。这样就可以将问题规模减小。

   合并后的点(即集合)的属性如何变化呢?假如设fact[i]表示集合的Ci和,iNum[i]表示i所属集合的结点个数;那么把fact[i]/iNum[i]作为贪心原则,其值大者先合并到其父节点,最终合并成一个集合。答案是怎么得出来的我看了很长时间才明白。先看一下下面的例子;

先初始化fact[i]=Ci, iNum[i]=1;将每一个点看成一个集合。

按照贪心原则首先选择5合并到3形成集合A={3,5},fact[A]=5,iNum[A]=2;

Ans+=fact[5]*iNum[3]=4;

然后再按照贪心原则选择集合A合并到1形成集合B={1,3,5},fact[B]=6,iNum[B]=3;

Ans+=fact[A]*iNum[1]=9;

然后再选择2合并到集合B形成集合C={1,2,3,5},fact[C]=8, iNum[C]=4;

Ans+=fact[2]*iNum[B]=15;

最后选择4合并到集合C形成集合D={1,2,3,4,5},fact[D]=10,iNum[D]=5;

Ans+=fact[4]*iNum[C]=23;

最后别忘了Ans+=fact[D]=33;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define MAXINT 1234567890
#define MAX 1001
struct node
{
int i,next,cost;
}verx[MAX];
int head[MAX],N,iNum[MAX],fact[MAX],father[MAX],visit[MAX];
int add(int s,int w)
{
verx[N].i=w;
verx[N].cost=0;
verx[N].next=head[s];
return N++;
}
int find(int n,int root)
{
int i,k;
double max=0;
for(i=1;i<=n;i++)
if(!visit[i] && i!=root && max<(double)fact[i]/iNum[i])
{
max=(double)fact[i]/iNum[i];
k=i;
}
return k;
}
int Union(int k,int pre)
{
int j;
fact[pre]+=fact[k];
iNum[pre]+=iNum[k];
for(j=head[k];j;j=verx[j].next)
father[verx[j].i]=pre;
return 0;
}
int work(int n,int root)
{
int i,k,pre,sum=0;
for(i=1;i<n;i++)
{
k=find(n,root);//查找最大平均值
visit[k]=1;
pre=father[k];
while(visit[pre]) pre=father[pre];//查找father[k]所在的集合

sum+=iNum[pre]*fact[k];
Union(k,pre);
}
return sum+fact[root];
}
int main()
{
int i,m,n,s,w;
while(scanf("%d%d",&n,&m) && m+n)
{
N=1;
memset(head,0,sizeof(head));
memset(visit,0,sizeof(visit));
for(i=1;i<=n;i++)
{
scanf("%d",&fact[i]);
iNum[i]=1;
}
for(i=1;i<n;i++)
{
scanf("%d%d",&s,&w);
head[s]=add(s,w);
father[w]=s;
}

printf("%d\n",work(n,m));
}
return 0;
}

 

posted @ 2012-02-19 21:27  书山有路,学海无涯  阅读(2321)  评论(0编辑  收藏  举报