FarmCraft,又是Dp

杀马特。学长 韩版系。学妹 提交于 2020-04-06 13:05:30

题目依然链接

题意:

  从根节点出发,每条边走两遍回到根节点,走边用时1,到达某个节点之后开始计时,到该节点最大的计时数时结束,回到根节点时根节点开始计时。求让所有计时都结束的最小时间。

Solve:

  很容易想到树形Dp,但是怎么Dp呢,因为记时开始后你还可以移动,所以并不是子树的简单累加,而是某种方法取max,我们先把状态定义下来,Dp[i]表示把以i为根的子树全部计时结束后需要的最小时间,那么我们要想一下怎么转移。

  如果我们已经知道了遍历顺序我们可以怎么转移,那就是Dp[i]=max(a[i],Dp[son1]+has1*2-1,Dp[son2]+has2*2-1+...+Dp[sonn]+hasn*n-1),hasj表示走到第j个儿子总共走的边数。

  为什么也是显而易见的,因为计时是同步的,所以我们只需取max就可以了。

  某大佬:我觉得有问题。

  我:一会儿为您解释。。。

  好的这个不太严谨的转移方程就写出来了,注意,是不太严谨的。

  那么我们怎么确定走儿子的顺序呢?这个再枚举就不太好了,我们要想一个好点的办法,这里其实很好想到要根据某个值进行排序,根据什么呢?其实这个只要稍微画画图就能想明白,如果Dp[i]+2*son[j]>Dp[j]+2*son[i].则i在j前面,这个怎么想到的呢,可以先画一下两个儿子的情况(这种很好证明)再类比一下,类别出结果再进行证明,怎么证明呢,这是我的证明方法:如果j在i前面,我们交换i和j的顺序至少不会更差(可以自己推推不等式,不难),所以我们可以直接让i在j前面,然后又有一个小小的问题,这样定义<会出问题吗,比如出现a>b且b<a或a>b且b>c且c>a,使sort函数死掉。其实并不会Dp[i]+2*son[j]>Dp[j]+2*son[i]就等价于Dp[i]-2*son[i]>Dp[j]-2*son[j]而Dp[i]-2*son[i]对于每个i是一个定值,所以并没有任何问题。

  好了顺序我们知道了,然后转移方程我们也知道了,但是刚才也有说到:转移方程不是很严谨(如下树,权值均为1),怎么让它严谨起来呢,很简单,感谢题目,让我们不用做任何改动就可以严谨起来(题目要求最后回来再计时根),想一想为什么。

  当然因为这个,我们也要做一些其他操作,这个各位应该都能想到解决方案。

  然后就是用不用开long long,多开当然没事,但是这一题不用开,不是数据水因为Dp最大也超不过n*2+max(a[i]),超不了int,但是多开一些没问题。

代码:

  

#include <cstdio>
#include <algorithm>
#include <string>
using namespace std;
const int maxn=500000+10;
struct E{
    int next;
    int to;
    E(){
        next=to=0;
    }
}ed[maxn*2];
int a[maxn];
int head[maxn];
int Dp[maxn];
int jl[maxn];
int er[maxn];
bool Cm(int a,int b){
    return Dp[a]+er[b]*2>Dp[b]+2*er[a];//对什么进行排序想清楚
}
int Dfs(int x,int fa){
    for(int i=head[x];i;i=ed[i].next){
        if(ed[i].to==fa)
            continue;
        else{
            Dfs(ed[i].to,x);
            er[x]+=er[ed[i].to]+1;
        }
    }
    int js=0;
    for(int i=head[x];i;i=ed[i].next){//要先Dfs再记录,前面的题里有提到
        if(ed[i].to==fa)
            continue;
        else{
            js++;
            jl[js]=ed[i].to;
        }
    }
    if(!js){
        Dp[x]=a[x];
        return 0;
    }
    sort(jl+1,jl+1+js,Cm);
    Dp[x]=a[x];
    int has=0;
    for(int i=1;i<=js;i++){
        has++;
        Dp[x]=max(Dp[jl[i]]+2*has-1,Dp[x]);
        has+=er[jl[i]];
    }
    return er[x];
}
int tot;
void J(int a,int b){
    tot++;
    ed[tot].to=b;
    ed[tot].next=head[a];
    head[a]=tot;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int js1,js2;
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&js1,&js2);
        J(js1,js2);
        J(js2,js1);
    }
    int js=a[1];//为保证最后再弄第一个的一些操作
    a[1]=0;
    Dfs(1,0);
    printf("%d",max(js+2*n-2,Dp[1]));
    return 0;
} 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!