[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 }