2054 贪心+并查集

贪心的总体思路是:每次找到一个权值最大的节点,如果它是根节点,则首先对它染色,
否则的话我们可以得出一个结论,在对它的父亲已经染色的情况下,立刻给它染色是最优的。
现在重点讨论第二种情况,当它不是根节点时,我们如果对它父亲染了色,则一定会立刻对它染色,
所以可以把它和它父亲合并为同一个节点,它和它父亲的儿子都成为了新节点的儿子,它的父亲的父亲则是新节点的父亲。
为了能继续贪下去,我们给每个节点赋上两个权值,
num_node表示对第i个节点图色所需的时间(第i个节点实际包含的节点数),sumc表示第i个节点的总权值(第i个节点实际包含的节点的权值和)。贪心目标是sumc/num_node的原因应该是因为如果想要为一个节点涂色,必须将一条路线都涂色。

题目样例分析:
粉刷顺序与合并顺序是不一样的,
粉刷顺序为: 1,3,5,2,4
合并顺序为:
          fa          num_node(包含点数) sumc(当前权值) ans(当前花费)
5         3              1                 4                4         他父亲(3)加4+4
3         1              2                 5                9         他父亲(1)加14=9*1+5
2         1              1                 2                2         他父亲(1)加8=3*2+2
4         1              1                 2                2         他父亲(1)加10=4*2+2
最后33 = 1 + 14 + 8 + 10

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

struct node{
	int num_node;  //此集合中包含点数
	int father;    //父亲
	int sumc;       //总权值
	int ans;        //它和它的子节点所用花费
	int flag;       //表示它已访问过
}Node[1001];
int find(int p) {//Tree[p].father如果不是根节点且Tree[p].father已粉刷就能继续往上找
	if (Node[p].father != p&&Node[Node[p].father].flag)
		Node[p].father = find(Node[p].father);
	return Node[p].father;

}
int main() {
	int N, R;
	while (~scanf("%d %d", &N, &R)) {
		memset(Node, 0, sizeof(Node));
		if (N == 0 && R == 0)
			break;
		for (int i = 1; i <= N; i++) {
			scanf("%d", &Node[i].sumc);
			Node[i].ans = Node[i].sumc;
			Node[i].num_node = 1;
		}
		Node[R].father = R;
		for (int i = 1; i < N; i++) {
			int f, s;
			scanf("%d %d", &f, &s);
			Node[s].father = f;
		}
		int index;
		double maxv;
		for (int i = 1; i < N; i++) {
			maxv = 0;
			for (int j = 1; j <= N; j++) {
				if (!Node[j].flag&&maxv < Node[j].sumc*1.0 / Node[j].num_node&&j != R) {//找到未被访问的平均花费最大的节点
					index = j;
					maxv = Node[j].sumc*1.0 / Node[j].num_node;
				}
			}
			Node[index].flag=1;
			int father = find(index);
			Node[father].ans += Node[index].ans + Node[index].sumc*Node[father].num_node;
			//重要:父亲的总花费=孩子总花费+孩子总权值*父亲的顺序值
			Node[father].sumc += Node[index].sumc;
			Node[father].num_node += Node[index].num_node;
		}
		printf("%d\n", Node[R].ans);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_40061421/article/details/83149362
今日推荐