P3574 [POI2014]FARFarmCraft

题目链接

题意分析

做这道题的时候一定要明白 你所负责的是运输 安装是原住民的事情 是可以同时运输+多个安装

我们设置dp[x] 表示走完以x为根的子树 并且所有人都安装完之后的最大时间

设置time[x] 表示走完以x为根的子树所用的时间

那么当前的转移就是 其中y是x的儿子 sum表示走完y之前x的所有子树单纯运输所用的时间 +1表示从x到y

\[dp[x]=max(dp[x],dp[y]+sum+1) \]

表示以y为根的子树中的点在走完之前的所有子树之后再安装

我们可以发现dp[x]的值和运输顺序有关 那么怎么确定运输顺序?

我们考虑一下 dp[x]是以x为根的子树都安装完的最大时间 而time[x]是以x为根的子树走完所用的时间

显然dp[x]≥time[x] 而dp[x]-time[x]表示运输完之后的闲置等待时间

我们希望先搞完闲置等待时间最多的 这样我们就可以在这些时间里做更多的事情 时间利用率更高

所以对于儿子 我们按照dp[x]-time[x]从大到小排序 然后转移

1.初始的时候dp[i]=ci(i≥2) 表示最大时间就是安装完所用的时间

2.为什么转移时是+1而不是+2

因为dp[x]-time[x]是等待时间 由于ci≥1 所以等待时间一定包含了从y返回x的时间

CODE:

#include<bits/stdc++.h>
#define M 1008611
using namespace std;
int n,tot;
int num[M];
long long dp[M],tim[M];
int to[M],nex[M],head[M];
int tmp[M];
void add(int x,int y)
{to[++tot]=y;nex[tot]=head[x];head[x]=tot;}
bool cmp(const int &A,const int &B)
{return dp[A]-tim[A]>dp[B]-tim[B];}
void dfs(int now,int fat)
{
	int cnt=0;
	for(int i=head[now];i;i=nex[i])
	{
		int v=to[i];
		if(v==fat) continue;
		dfs(v,now);
		tim[now]+=tim[v]+2;
	}
	for(int i=head[now];i;i=nex[i])
	{
		int v=to[i];
		if(v==fat) continue;
		tmp[++cnt]=v;
	}
	long long sum=0;
	sort(tmp+1,tmp+cnt+1,cmp);
	for(int i=1;i<=cnt;++i)
	{
		dp[now]=max(dp[now],sum+1+dp[tmp[i]]);
		sum+=tim[tmp[i]]+2;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&num[i]);
	for(int i=1,x,y;i<n;++i)
	{
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x); 
	}
	for(int i=2;i<=n;++i) dp[i]=num[i];
	dfs(1,0);
	printf("%lld\n",max(dp[1],tim[1]+num[1]));
	return 0;
}
原文地址:https://www.cnblogs.com/tcswuzb/p/14396490.html