[POI2014]FAR-FarmCraft (树规+贪心)
题干
In a village called Byteville, there are houses connected with N-1 roads. For each pair of houses, there is a unique way to get from one to another. The houses are numbered from 1 to . The house no. 1 belongs to the village administrator Byteasar. As part of enabling modern technologies for rural areas framework, computers have been delivered to Byteasar’s house. Every house is to be supplied with a computer, and it is Byteasar’s task to distribute them. The citizens of Byteville have already agreed to play the most recent version of FarmCraft (the game) as soon as they have their computers. Byteasar has loaded all the computers on his pickup truck and is about to set out to deliver the goods. He has just the right amount of gasoline to drive each road twice. In each house, Byteasar leaves one computer, and immediately continues on his route. In each house, as soon as house dwellers get their computer, they turn it on and install FarmCraft. The time it takes to install and set up the game very much depends on one’s tech savviness, which is fortunately known for each household. After he delivers all the computers, Byteasar will come back to his house and install the game on his computer. The travel time along each road linking two houses is exactly 1 minute, and (due to citizens’ eagerness to play) the time to unload a computer is negligible. Help Byteasar in determining a delivery order that allows all Byteville’s citizens (including Byteasar) to start playing together as soon as possible. In other words, find an order that minimizes the time when everyone has FarmCraft installed.
INPUT
OUTPUT
The first and only line of the standard output should contain a single integer: the (minimum) number of minutes after which all citizens will be able to play FarmCraft together.
奇妙的翻译
mhy住在一棵有n个点的树的1号结点上,每个结点上都有一个妹子。
mhy从自己家出发,去给每一个妹子都送一台电脑,每个妹子拿到电脑后就会开始安装zhx牌杀毒软件,第i个妹子安装时间为。
树上的每条边mhy能且仅能走两次,每次耗费1单位时间。mhy送完所有电脑后会回自己家里然后开始装zhx牌杀毒软件。
卸货和装电脑是不需要时间的。
求所有妹子和mhy都装好zhx牌杀毒软件的最短时间。
(哪里冒出来的妹子???)
瞎想:
- 每条边都只能走两次,而且我们最后必须回到老家,意味着我们必须一次遍历完整棵树(包括每棵子树)
- 读完题我们要get到一个很重要的点:装软件和送电脑是可以同时进行的(
妹子居然会装电脑!) - 我们假设一个节点有两个子节点A和B,定义 f[i] 为遍历完子树后再回到 i 点的最短用时,siz[i] 为 i 的子树的总大小,那么,我们可以得到:
- 先去遍历A的子树,总时间:siz[A]*2 + f[B] ;//当然这里其实是要+1的,因为我们从A或B到 i 还需要再走一步,看我下面代码可以知道,这里不影响
- 先去遍历B的子树,总时间:siz[B]*2 + f[A] ;
- 我们用结构体记录下所有子树的( f[i] , siz[i]*2)
- 按照 time +p.siz < p.time + siz 从小到大排序
- 理由:如果先遍历A更合适,有 siz[A]*2+f[B] < siz[B]*2+f[A]; //这里之前脑子一抽写反了,希望没误导大家…
- 这里我觉得解释一下比较好,相比较而言,我们肯定更愿意先去安装软件耗时更多而跑图用时更少的子树,因为这样我们就可以让他们在我们给别的地方送电脑的时候自行安装,从而节省时间。那么在这里,我们假设f[A]和f[B]相等,那么此时siz[B]肯定是大于siz[A]的,这意味这A和B的总用时相等,但我们遍历B时耽搁在路上的时间更多(子树规模更大嘛),所以相比较而言A的装软件用时更长,也就是我们应该先走A;
- 最后的答案 f[x]=max(w[x],f[v]+sum)
这里我们结合代码分析一下:
struct node{
int time,siz;
bool operator <(const node& p)const{
return p.time+siz<time+p.siz;
}
}t[maxn];
void dfs(int u,int fa){
siz[u]=1;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
}
int tot=0;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(v==fa) continue;
tot++;//儿子数量
t[tot].time=f[v]+1;//从v回到u需加1
t[tot].siz=siz[v]*2;//这里乘2,下面计算时就不用乘2了,减少失误
}
sort(t+1,t+1+tot);
int sum=0,maxx=(siz[u]-1)*2;//sum依然是目前跑路所需总时间,maxx初始为单纯跑完u的子树所需总时间
for(int i=1;i<=tot;i++){
maxx=max(maxx,t[i].time+sum);//更新目前最大用时
sum+=t[i].siz;//累加跑路时间
}
f[u]=max(w[u],maxx);//如果单单u点装软件用时比完全弄完所有子树都费事,直接取w[u]
}
完整代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=5*1e6+10;
int n,f[maxn],head[maxn],len=0,siz[maxn];
int w[maxn];
struct Edge{
int next,to;
}edge[maxn<<1];
void Add(int u,int v){
edge[++len].next=head[u];
edge[len].to=v;
head[u]=len;
}
struct node{
int time,siz;
bool operator <(const node& p)const{
return p.time+siz<time+p.siz;
}
}t[maxn];
void dfs(int u,int fa){
siz[u]=1;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
}
int tot=0;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(v==fa) continue;
tot++;
t[tot].time=f[v]+1;
t[tot].siz=siz[v]*2;
}
sort(t+1,t+1+tot);
for(int i=1;i<=tot;i++)
printf("%d %d\n",t[i].time,t[i].siz);
int sum=0,maxx=(siz[u]-1)*2;
for(int i=1;i<=tot;i++){
maxx=max(maxx,t[i].time+sum);
sum+=t[i].siz;
}
f[u]=max(w[u],maxx);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
Add(u,v),Add(v,u);
}
dfs(1,0);
int ans=0;
ans=max(f[1],w[1]+(n-1)*2);
cout<<ans<<endl;
return 0;
}