CF526B Om Nom and Dark Park 题解

题目:CF526B Om Nom and Dark Park

图论 - dfs - 树 - 贪心

题目大意

给出一个有 2 n + 1 1 2^{n+1}-1 个节点的满二叉树,边带权
要求让一些边的边权增加,使得所有叶子节点到跟的路径上的边权之和相等。并让增加的权值最小

解题思路

这里讲一个非常贪心的做法

首先,我们找出离根节点最远的叶子节点,并算出他的距。在以后的计算中将不会在该路径上增加边权(不然会出现浪费)

接着,再使用 dfs 求出每个叶子节点到根的路径上,还需要加多少权值,我们用 d i s [ ] dis[] 表示

对于非叶子节点的 d i s [ ] dis[] ,我们用来维护以该节点为根的子树中所有叶子节点的 d i s [ ] dis[] 最小值

void dfs1(int x,int cur)
{
	tot=max(tot,cur); // tot 维护离根节点最远的点的距离
	for(int i=0;i<e[x].size();++i)
	{
		int y=e[x][i].v;
		dfs1(y,cur+e[x][i].len);
	}
}
void dfs2(int x,int cur) // 求出所有叶子和非叶子节点的 dis[]
{
	if(!e[x].size())
	{
		dis[x]=tot-cur; // 注意:叶子节点的 dis 储存的是还需要加多少边权
		return;
	}
	dis[x]=inf;
	for(int i=0;i<e[x].size();++i)
	{
		int y=e[x][i].v;
		dfs2(y,cur+e[x][i].len);
		dis[x]=min(dis[x],dis[y]);
	}
}

然后,我们开始给边加权:

由于题目要求总共增加的权值最小,我们就要然加上的边权利用率最大
显然,边权加在离跟越近的地方,就可以让更多叶子节点收益,也就利用率越大。所以我们要尽可能地在离根近的地方给边加权

void dfs(int x,int cur) // cur 储存上方跟该子树有关的边已经加了多少权
{
	for(int i=0;i<e[x].size();++i)
	{
		int y=e[x][i].v;
		ans+=dis[y]-cur; // 上方已经加了 cur 的权,所以要减掉
		dfs(y,dis[y]);
	}
}

再给边加权是,要考虑到下方子树的所有叶子节点,不能加多(只能加上叶子节点中最小的 d i s [ ] dis[] )。这时,非叶子节点的 d i s [ ] dis[] 就派上用场了

完整代码

#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
const int Maxn=2100,inf=0x3f3f3f3f;
struct edge{
	int v,len;
	edge(int x,int y)
	{
		v=x,len=y;
	}
};
int dis[Maxn];
vector <edge> e[Maxn];
int n,m,tot,ans;
inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s*w;
}
void dfs1(int x,int cur)
{
	tot=max(tot,cur);
	for(int i=0;i<e[x].size();++i)
	{
		int y=e[x][i].v;
		dfs1(y,cur+e[x][i].len);
	}
}
void dfs2(int x,int cur)
{
	if(!e[x].size())
	{
		dis[x]=tot-cur;
		return;
	}
	dis[x]=inf;
	for(int i=0;i<e[x].size();++i)
	{
		int y=e[x][i].v;
		dfs2(y,cur+e[x][i].len);
		dis[x]=min(dis[x],dis[y]);
	}
}
void dfs(int x,int cur)
{
	for(int i=0;i<e[x].size();++i)
	{
		int y=e[x][i].v;
		ans+=dis[y]-cur;
		dfs(y,dis[y]);
	}
}
int main()
{
//	freopen("in.txt","r",stdin);
	m=read()+1,n=(1<<m)-1;
	
	for(int i=2;i<=n;++i)
	{
		int x=(i>>1),y=i,c=read();
		e[x].push_back(edge(y,c));
	}
	
	dfs1(1,0);
	dfs2(1,0);
	dfs(1,0);
	
	printf("%d\n",ans);
	
	return 0;
}
发布了75 篇原创文章 · 获赞 2 · 访问量 1792

猜你喜欢

转载自blog.csdn.net/Brian_Pan_/article/details/104259429