BZOJ-1908 Pku2054 Color a Tree(贪心)
题目描述
一棵有 \(n(1\leq n\leq 1000)\) 个节点的树,每个节点 \(i(1\leq i\leq n)\) 都有一个权值 \(A[i](1\leq A[i]\leq 1000)\)。现在要把这棵树的节点全部染色,染色的规则是:根节点 \(R\) 可以随时被染色;对于其他节点,在被染色之前它的父节点必须已经染上了色。每次染色的代价为 \(T\times A[i]\),其中 \(T\) 代表当前是第几次染色。求把这棵树染色的最小总代价。
分析
有一个错误的贪心策略是:每一步在可以被染色的点里选权值最大的染色,很容易构造出反例:构造一棵树,一个权值很小的节点下边有很多权值巨大的节点,另一个权值较大的节点却没有子节点。
可以发现一个性质:树中除根节点外权值最大的点,一定会在它的父节点被染色后立即染色。
于是我们可以确定的是,树中权值最大的点及其父节点的染色操作是连续进行的,可以把这两个点合并起来。合并得到的新点的权值设为这两个点的权值的平均值。
例如有权值为 \(x,y,z\) 的三个点,我们已知 \(x\) 和 \(y\) 的染色操作是连续进行的,那么就有两种可能的染色方案:
\(1.\) 先染 \(x,y\),再染 \(z\),代价是 \(x+2y+3z\)。
\(2.\) 先染 \(z\),再染 \(x,y\),代价是 \(z+2x+3y\)。
我们主要关心这两个代价之间的大小关系,所以不妨把两个式子同时加上 \((z-y)\) 再除以 \(2\),分别得到:
\(1.\) 代价 \((x+y)/2+2\times z\)。
\(2.\) 代价 \(z+2\times ((x+y)/2)\)。
这恰好就相当于有权值为 \((x+y)/2\) 和 \(z\) 的两个点的两种染色次序。换言之,下列两种情况的最优染色次序可以相互转化:
\(1.\) 权值为 \(x,y,z\) 的三个点。
\(2.\) 权值为 \((x+y)/2\) 和 \(z\) 的两个点。
因此可以得到一种等效权值的算法:记录每个点是由多少个点合并而成的,一个点的等效权值定义为:该点包含的原始权值总和除以该点包含的原始点数。
不断在树中取等效权值最大的点 \(p\),与其父节点 \(fa\) 合并。合并之前 \(p\) 与 \(fa\) 各自包含的点的染色顺序是已知的,我们就让 \(p\) 中第一个点排在 \(fa\) 中最后一个点之后紧接着被染色,把这个顺序保存在 \(p\) 与 \(fa\) 合并以后的点上。最终整棵树合并成一个点后,我们就按照这个点内保存的顺序在原始的树上把各个节点依次染色,计算出花费的总代价,即为所求。
代码
#include<bits/stdc++.h>
using namespace std;
struct node
{
int sum;
int ans;
int cnt;
}tree[1010];
int fa[1010];
bool vis[1010];
int get(int x)
{
if(x!=fa[x]&&vis[fa[x]]==1)
return fa[x]=get(fa[x]);
return fa[x];
}
int main()
{
int n,root;
while(cin>>n>>root&&n&&root)
{
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;i++)
{
scanf("%d",&tree[i].sum);
tree[i].ans=tree[i].sum;
tree[i].cnt=1;
}
memset(fa,0,sizeof(fa));
memset(vis,0,sizeof(vis));
fa[root]=root;
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d %d",&x,&y);
fa[y]=x;
}
int pos=-1;
for(int i=1;i<=n-1;i++)
{
double maxn=0;
for(int j=1;j<=n;j++)
{
if(vis[j]||j==root)
continue;
double temp=1.0*tree[j].sum/tree[j].cnt;
if(temp>maxn)
{
maxn=temp;
pos=j;
}
}
vis[pos]=1;
int father=get(pos);
tree[father].ans=tree[father].ans+tree[pos].ans+tree[father].cnt*tree[pos].sum;
tree[father].sum=tree[father].sum+tree[pos].sum;
tree[father].cnt=tree[father].cnt+tree[pos].cnt;
}
cout<<tree[root].ans<<endl;
}
return 0;
}
posted on 2020-11-27 14:32 DestinHistoire 阅读(92) 评论(0) 收藏 举报
浙公网安备 33010602011771号