Acesrc and Travel(树上博弈dp)

http://acm.hdu.edu.cn/showproblem.php?pid=6662

 题目描述

Acesrc is a famous tourist at Nanjing University second to none. During this summer holiday, he, along with Zhang and Liu, is going to travel to Hong Kong. There are n spots in Hong Kong, and n−1 bidirectional sightseeing bus routes connecting these spots. They decide to visit some spots by bus.

However, Zhang and Liu have different preferences for these spots. They respectively set a satisfactory value for each spot. If they visit the ith spot, Zhang will obtain satisfactory value ai, and Liu will obtain bi. Starting with Zhang, they alternately decide the next spot to visit for the sake of fairness. There must be a bus route between the current spot and the next spot to visit. Moreover, they would never like to visit a spot twice. If anyone can't find such a next spot to visit, they have no choice but to end this travel.

Zhang and Liu are both super smart competitive programmers. Either want to maximize the difference between his total satisfactory value and the other's. Now Acesrc wonders, if they both choose optimally, what is the difference between total satisfactory values of Zhang and Liu?

 

输入

The first line of input consists of a single integer T (1≤T≤30), denoting the number of test cases.

For each test case, the first line contains a single integer n (1≤n≤105), denoting the number of spots. Each of the next two lines contains n integers, a1,a2,⋯,an and b1,b2,⋯,bn (0≤ai,bi≤109), denoting the 
satisfactory value of Zhang and Liu for every spot, respectively. Each of the last n−1 lines contains two integers x,y (1≤x,y≤n,x≠y), denoting a bus route between the xth spot and the yth spot. It is reachable from any spot to any other spot through these bus routes.

It is guaranteed that the sum of n does not exceed 5.01×105.

 

输出

For each test case, print a single integer in one line, the difference of total satisfactory values if they both choose optimally.

 

样例输入

复制样例数据

1
3
1 1 1
0 2 3
1 2
1 3

样例输出

-1

题意:树上有n个点,A到达i点能获得ai欢乐值,B到达i点能获得bi欢乐值,每个人都想让自己手中的欢乐值尽量多,每个节点最多走一次,当无路可走的时候游戏结束,问A的欢乐值最多比B多多少

鲲神的题解:我们记录每个点的权值为ai-bi,这样先手就是求权值和最大,后手就是权值和最小,所以第一次dfs保存每个节点到叶节点的最大值,次大值,最小值,次小值,更新当前节点最大值时,是通过孩子的最小值来更新,更新最小就是通过孩子的最大值来更新,然后第二遍dfs就是通过父亲来更新孩子,很多细节要多注意,,。。

--------------------- 
版权声明:本文为CSDN博主「mmk27」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mmk27_word/article/details/99597045

这道题细节好多,写的好晕

1.当往子树方向走的时候,是用子树的最小值更新根节点的最大值,用子树的最大值更新根节点的最小值(因为下一步是另一个人走的,他肯定会往对自己更有利的方向走)

2.当往父亲方向走的时候,是用父亲方向的最大值更新最大值,父亲方向的最小值更新最小值(因为既然是求往父亲方向走,那么下一个人一定要往父亲走,在父亲节点上再往哪儿走还是自己说了算)

3.能不能往父亲方向走(能不能从父亲方向获得值),能不能从次大(次小)方向得到值,这些一定要明确,不然会出错

2节点的父亲节点是1节点, 但是1并没有父亲(也就是说无法从1的父亲方向得到值),只能往3走

如果往1的父亲方向走(初值赋的不好的时候,比如说0),那么走到1就不走了,与题意不符

4.标记叶子节点,并不能往子树方向走

5.ans=max(ans,min(dp1[i][0],dp1[i][1])),因为下一步是另一个人说了算,他想使权值和尽量小

#include<bits/stdc++.h>
#pragma GCC optimize(3)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll dp1[N][3],dp2[N][3];
int id1[N],id2[N];
ll val[N];
struct Edge{
	int v,next;
}e[N<<1];
int head[N],tot=0;
bool book[N];
void init(int n){
	tot=0;
	for(int i=1;i<=n;i++) {
		head[i]=-1;
		book[i]=true;
	}
} 
void add(int u,int v){
	e[tot].v=v;
	e[tot].next=head[u];
	head[u]=tot++;
}
void dfs1(int u,int fa){
	dp1[u][1]=dp1[u][2]=1e18;
	dp2[u][1]=dp2[u][2]=-1e18; 
	for(int i=head[u];~i;i=e[i].next){
		int &v=e[i].v;
		if(v==fa) continue;
		dfs1(v,u);
	    book[u]=false; 
		if(dp2[v][1]<dp1[u][1]){//用子节点的最小值更新该节点的最大值 
			dp1[u][1]=dp2[v][1];
			id1[u]=v;
		}
		if(dp1[v][1]>dp2[u][1]){//用子节点的最大值更新该节点的最小值 
			dp2[u][1]=dp1[v][1];
			id2[u]=v;
		}
	}
	for(int i=head[u];~i;i=e[i].next){
		int &v=e[i].v;
		if(v==fa) continue;
		if(v!=id1[u]&&dp2[v][1]<dp1[u][2]){
			dp1[u][2]=dp2[v][1];
		}
		if(v!=id2[u]&&dp1[v][1]>dp2[u][2]){
			dp2[u][2]=dp1[v][1];
		}
	}
	if(book[u]) {
		dp1[u][1]=dp2[u][1]=val[u];
	}
	else{
		dp1[u][1]+=val[u];
		if(dp1[u][2]!=1e18) dp1[u][2]+=val[u];
		dp2[u][1]+=val[u];
		if(dp2[u][2]!=-1e18)dp2[u][2]+=val[u];
	}
}
void dfs2(int u,int fa){
	for(int i=head[u];~i;i=e[i].next){
		int &v=e[i].v;
		if(v==fa) continue;
		if(v==id2[u]){//dp1[v][0]=max(dp2[u][0],dp2[u][2])+w
			dp1[v][0]=-1e18;
			if(dp2[u][2]!=-1e18) dp1[v][0]=dp2[u][2]+val[v];
			if(dp2[u][0]!=1e18) dp1[v][0]=max(dp1[v][0],dp2[u][0]+val[v]);
			if(dp1[v][0]==-1e18) dp1[v][0]=val[u]+val[v];
		}
		else{//dp1[v][0]=max(dp2[u][0],dp2[u][1])+w
			dp1[v][0]=dp2[u][1]+val[v];
			if(dp2[u][0]!=1e18) dp1[v][0]=max(dp1[v][0],dp2[u][0]+val[v]);
		}
		if(v==id1[u]){ //dp2[v][0]=min(dp1[u][0],dp1[u][2])+w
		    dp2[v][0]=1e18;
			if(dp1[u][2]!=1e18) dp2[v][0]=dp1[u][2]+val[v];
			if(dp1[u][0]!=-1e18) dp2[v][0]=min(dp2[v][0],dp1[u][0]+val[v]);
			if(dp2[v][0]==1e18) dp2[v][0]=val[u]+val[v];
		}
		else{ //dp2[v][0]=min(dp1[u][0],dp1[u][1])+w 
			dp2[v][0]=dp1[u][1]+val[v];
			if(dp1[u][0]!=-1e18) dp2[v][0]=min(dp2[v][0],dp1[u][0]+val[v]);
		}
		
		dfs2(v,u);
	}
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
    	int n;
    	scanf("%d",&n);
    	init(n);
    	for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
    	for(int i=1;i<=n;i++){
    		ll b;
    		scanf("%lld",&b);
    		val[i]-=b;
		}
		for(int i=1;i<n;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			add(u,v);
			add(v,u);
		}
		dfs1(1,0);
		dp1[1][0]=-1e18;
		dp2[1][0]=1e18;
		dfs2(1,0);
		ll ans=dp1[1][1];//根只有下,没有上 
		for(int i=2;i<=n;i++){
			if(!book[i]) ans=max(ans,min(dp1[i][0],dp1[i][1]));
			else ans=max(ans,dp1[i][0]);//叶子只有上没有下 
		}
		printf("%lld\n",ans);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值