Color a Tree

C o l o r   a   T r e e Color\ a\ Tree Color a Tree

题目链接:POJ 2054

题目大意

一颗有 n n n个节点的树,每个节点 i i i都有一个权值 A [ i ] A[i] A[i]。现在要把这棵树的节点全部染色,染色的规则是:根节点 R R R可以随时被染色;对于其他节点,在被染色之前它的父亲节点必须已经染上了色。每次染色的代价为 T ∗ A [ i ] T*A[i] TA[i],其中 T T T代表当前是第几次染色。求把这棵树染色的最小总代价。

样例输入

5 1
1 2 1 2 4
1 2
1 3
2 4
3 5
0 0

样例输出

33

数据范围

1 < = N < = 1000 1 <= N <= 1000 1<=N<=1000
1 < = R < = N 1 <= R <= N 1<=R<=N
1 < = A [ i ] < = 1000 1<=A[i]<=1000 1<=A[i]<=1000

思路

这道题是一道贪心,但是还是要用到并查集。
我们可以通过思考先到,树中权值最大的点与其父亲节点的染色是连续进行的,我们可以“合并”这两个点,新点权值为这两个点的权值的平均值。
我们不断找树中权值最大的那个点,让它与其父亲节点“合并”。先染色父亲,才能染色儿子,我们就按这个顺序存在“合并”后的点上。
最后,我们按照最后一个点的顺序依次染色,求出总代价,就可以了。

代码

#include
#include
using namespace std;
struct point
{
	int num,sum,ans,father;
}a[1010];
int n,r,x,y,son,father;
double maxn;
bool in[1010];
int find(int x)//并查集找父亲
{
	if (x!=a[x].father&&in[a[x].father])
	{
		a[x].father=find(a[x].father);
	}
	return a[x].father;
}
int main()
{
	scanf("%d%d",&n,&r);//读入
	while (n&&r)
	{
		memset(a,0,sizeof(a));//初始化
		memset(in,0,sizeof(in));//初始化
		for (int i=1;i<=n;i++)
		{
			scanf("%d",&a[i].sum);//读入
			a[i].ans=a[i].sum;//初始化
			a[i].num=1;//初始化
		}
		for (int i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);//读入
			a[y].father=x;//标记
		}
		a[r].father=r;//初始化
		for (int i=1;i<n;i++)
		{
			maxn=0;//初始化
			for (int j=1;j<=n;j++)//找儿子
			 if (!in[j]&&j!=r)
			  if (maxn<(double)a[j].sum/(double)a[j].num)
			  {
			  	son=j;
				maxn=(double)a[j].sum/(double)a[j].num;
			  }
			in[son]=1;//标记
			father=find(son);//找爸爸
			a[father].ans+=a[son].ans+a[son].sum*a[father].num;//爸爸的代价要加儿子的代价和儿子的总权值乘爸爸所包含的节点数
			a[father].sum+=a[son].sum;//更新父亲总权值
			a[father].num+=a[son].num;//更新父亲包含的节点数
		}
		printf("%d\n",a[r].ans);//输入
		scanf("%d%d",&n,&r);//输出
	}
	return 0;
}

你可能感兴趣的:(#,并查集,#,贪心)