[hdu-6662]Acesrc and Travel 树形DP 2019多校8

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6662

题目大意:一棵树,点权是一个差值a-b,两个人(张和刘)在树上旅游,每个点只能走过一次,张想要走过的节点和最大,刘要走过的和最小,张先手然后两人交替选择下一个相邻的点走直到无路可走停止,两个人都会选择对自己的最优策略。求最大的和。

ps:题目原意是张在每一个节点有一个满意度,刘有一个满意度,求满意度的差别最大。然后我理解成了两个人都想要差别的绝对值最大qwq,比赛时理解错题写错了

题解:树形dp

每段旅程的终点为叶子结点

固定好树后,以每个节点为起点,有两种走法,向上和向下 我们要选择其中最优的

1.  向下维护:一遍dfs

maxx[v] : v点张选择往下走得最大值

minn[v]  : v点刘选择往下走得最小值

maxx[v]=  max( minn[ son ] ) +  v[i]    张会选择往后 刘做选择 得到的最大的

minn[v]=   min( maxn[ son ] ) +  v[i]    刘会选择往后 张做选择 得到的最小的

2.向上维护:要考虑当前点的后手(父亲)会做对他最优的选择

当前点父亲  可以是沿着父亲一直往上走,或者向下走到兄弟的最优   

如果当前点是父亲往下走最优时的那条路,就是往上走或者走父亲向下的 次优

zhang[v] : v点张选择往上走得最大值

liu[v]  : v点刘选择往上走得最小值

zhang[v] = min( liu [ father ] , minn[ father ] )    当前张做选择往上走  父亲刘会选择  向上 或 向下(1中维护过) 中更小的一个走

liu[v] = max( zhang [ father ], maxx[ father ] )    当前刘做选择往上走  父亲张会选择 向上 或 向下  中更大的一个走

 

用每个点liu做选择的最优答案(说明这个起始点是张选的)更新答案

 

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const maxn=1e5+100;
int tot,n,head[maxn],fa[maxn];
long long  ans,son[maxn],a[maxn],minn[maxn],maxx[maxn],fmi[maxn],fmaa[maxn],danma[maxn],danmi[maxn];
ll zhang[maxn],liu[maxn];
//max[x]当前x点往下走张做选择 min[x] 当前点x往下走刘做选择
//zhang[x]当前点往上走张做选择 liu[x] 往上走刘做选择
struct edge{
    ll to, nex;
}e[maxn<<1];
void builde(int x,int y){
    e[++tot].to=y,e[tot].nex=head[x];head[x]=tot;
}
inline int  get_num(){
    char ch;
    int num=0;
    ch=getchar();
    while(ch<'0'||ch>'9'){ch=getchar();}
    while(ch>='0'&&ch<='9'){num=(num<<3)+(num<<1)+ch-'0';ch=getchar();}
    return num;
}
void dfs(int x){
    ll nmin=2e18,nmax=-(2e18),nit=2e18,nat=-(2e18);//记录前两大和前两小
    int to;
    minn[x]=maxx[x]=danmi[x]=danma[x]=a[x];
    for(int i=head[x];i;i=e[i].nex){
        to=e[i].to;
        if(to==fa[x])continue;
        fa[to]=x;
        dfs(to);
        son[x]++;
        if(maxx[to]<nmin){ nit=nmin, nmin=maxx[to],fmi[x]=to;}
        else if(maxx[to]<nit)nit=maxx[to];//刘会选择后手张做选择中最小的
        if(minn[to]>nmax){ nat=nmax, nmax=minn[to],fmaa[x]=to;}
        else if(minn[to]>nat)nat=minn[to];//张会选择后手刘做选择中最大的
    }
    if(son[x]){
        maxx[x]+=nmax,minn[x]+=nmin;
    }
    if(son[x]>1){
        danmi[x]=nit+a[x];danma[x]=nat+a[x];
    }
}
void dfs2(int x){
    for(int i=head[x];i;i=e[i].nex){
        int to=e[i].to;
        if(to==fa[x])continue;
        if(son[x]==1){
            zhang[to]=liu[x]+a[to];//如果儿子只有一个,那这个儿子只能沿着父亲往上走
            liu[to]=zhang[x]+a[to];
        }
        else{//如果有多个儿子,它可以沿着父亲往上走,或者走他的兄弟中对他最优的,【注意如果是原先父亲结点的最优来源,那应该走次优的那条兄弟路线
            ll rr=(to==fmaa[x])?rr=danma[x]:maxx[x];
            ll lu=(to==fmi[x])?lu=danmi[x]:minn[x];
            if(x!=1)rr=max(zhang[x],rr),lu=min(liu[x],lu);//
            liu[to]=rr+a[to];
            zhang[to]=lu+a[to];
        }
        dfs2(to);
    }
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        memset(head,0,sizeof head);
        memset(son,0,sizeof(son));
        memset(fmi,0,sizeof fmi);
        memset(fmaa,0,sizeof fmaa);
        scanf("%d",&n);
        tot=0;
        for(int i=1;i<=n;i++)a[i]=get_num();
        for(int i=1;i<=n;i++)a[i]-=get_num();
        int p,q;
        for(int i=1;i<n;i++){
            p=get_num(),q=get_num();
            builde(p,q);builde(q,p);
        }
        
        dfs(1);
        zhang[1]=liu[1]=a[1];ans=minn[1];
        dfs2(1);
        //每个点只能用liu(起始点刘做选择说明起始点是张选的)更新一次答案,用上下走或向上走最优的那个
        for(int i=2;i<=n;i++){
            if(son[i])ans=max(ans,min(liu[i],minn[i]));//非儿子可以选择往上或往下走对自己最优的
            else ans=max(ans,liu[i]);//儿子做起点只能往上走
        }
        //for(int i=1;i<=n;i++)cout<<maxx[i]<<' '<<minn[i]<<endl;
        printf("%lld\n",ans);
    }
    return 0;
}

标程:

 1 #include<bits/stdc++.h>
 2 #define maxn 202000
 3 #define x first
 4 #define y second
 5 
 6 using namespace std;
 7 typedef long long ll;
 8 typedef pair<ll,ll> pi;
 9 const ll inf=1e16;
10 ll a[maxn],n,k,query,ans,p[maxn],q[maxn],d[maxn];
11 vector <int> h[maxn];
12 pi f[maxn],g[maxn];
13 
14 void dfs(int fa,int u)
15 {
16     for (int i=0;i<h[u].size();i++)
17     {
18         int v=h[u][i];
19         if (v==fa) continue;
20         dfs(u,v),d[u]++;
21         if (f[u].x<a[u]+g[v].x) f[u].y=f[u].x,f[u].x=a[u]+g[v].x;
22         else if (f[u].y<a[u]+g[v].x) f[u].y=a[u]+g[v].x;
23         if (g[u].x>a[u]+f[v].x) g[u].y=g[u].x,g[u].x=a[u]+f[v].x;//g先手
24         else if (g[u].y>a[u]+f[v].x) g[u].y=a[u]+f[v].x;
25     }
26     if (!d[u]) f[u].x=f[u].y=g[u].x=g[u].y=a[u];
27 }
28 
29 void dfs2(int fa,int u)
30 {
31     for (int i=0;i<h[u].size();i++)
32     {
33         int v=h[u][i]; ll r;
34         if (v==fa) continue;
35         if (d[u]==1) p[v]=q[u]+a[v],q[v]=p[u]+a[v];
36         else {
37             r=(f[u].x==g[v].x+a[u])?f[u].y:f[u].x;
38             if (u!=1) r=max(r,q[u]); p[v]=r+a[v];
39             r=(g[u].x==f[v].x+a[u])?g[u].y:g[u].x;
40             if (u!=1) r=min(r,p[u]); q[v]=r+a[v];
41         }
42         dfs2(u,v);
43     }
44 }
45 int main()
46 {
47     //freopen("test1.in","r",stdin);
48     //freopen("test2.out","w",stdout);
49     scanf("%lld",&query);
50     while (query--){
51         scanf("%lld",&n);
52         //printf("%d\n", n);
53         for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
54         for (int i=1;i<=n;i++) {scanf("%lld",&k); a[i]-=k;}
55         for (int i=1;i<=n;i++) h[i].clear(),d[i]=0,f[i].x=f[i].y=-inf,g[i].x=g[i].y=inf;
56         for (int i=1;i<n;i++)
57         {
58             int u,v; scanf("%d%d",&u,&v);
59             h[u].push_back(v);
60             h[v].push_back(u);
61         }
62         dfs(0,1); p[1]=q[1]=a[1]; dfs2(0,1); ans=g[1].x;
63         //for (int i=1;i<=n;i++) cout << p[i] << ' ' << q[i] << endl;
64         //for (int i=1;i<=n;i++) cout << f[i].x << ' ' << f[i].y << ' ' << g[i].x << ' ' << g[i].y << endl;
65         for (int i=2;i<=n;i++) if (d[i]) ans=max(ans,min(g[i].x,p[i])); else ans=max(ans,p[i]);
66         cout << ans << endl;
67     }
68     return 0;
69 }

 

posted @ 2019-08-15 13:57  conver^_^  阅读(322)  评论(0编辑  收藏  举报