【hiho1035】自驾旅行III

题目大意:给定一棵 N 个节点的有根树,1 号节点为根节点,树边有两个权值,分别为走路的代价和开车的代价。有一个旅行者开车要从根节点出发,必须遍历给定点集,可以在任何位置停止旅行,有车时可以选择开车或步行,没车只能跑路,求最小代价。

题解:这是我做过最恶心的树形dp QAQ
和 apple tree 这道题很相似,只不过这次是多了车这个东西。因此,在设计状态的时候要考虑到人和车的关系,dp[u][0] 表示人走到以 u 为根的子树中必须返回的最小权值,dp[u][1] 表示人下去但是不一定上来的最小代价,dp[u][2] 表示人和车都可以下去,人和车都必须上来的最小代价,dp[u][3] 表示人和车可以下去,人上来车不一定上来的最小代价,dp[u][4] 表示人和车可以下去,但是人和车都不一定上来的最小代价。

dp[u][0-3] 的转移方程比较好想,对于最后一种情况来说,分为走最后一棵子树的时候有没有车,如果没有车就意味着在之前遍历的某棵子树中只有人回来了,因此需要考虑两棵子树对答案的贡献。可是发现,两棵子树显然不能是同一棵,因此需要记录下最优解和次优解,以及取得最优解的是哪颗子树,这样才能合并两棵子树的贡献。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
typedef long long LL;

int n,m,key[maxn];
LL dp[maxn][5];
struct node{int nxt,to;LL w0,w1;}e[maxn<<1]; // w0 -> walk   w1 -> car
int tot=1,head[maxn];
inline void add_edge(int from,int to,LL w0,LL w1){
	e[++tot]=node{head[from],to,w0,w1},head[from]=tot;
}

void dfs(int u,int fa){
	LL d1=0,d3=0,d4=0;
	LL fi1=1e18,se1=1e18,fi2=1e18,se2=1e18; // fi1 -> dp[v][3]+w0+w1  fi2 -> dp[v][1]+w0
	int x=0,y=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to; LL w0=e[i].w0,w1=e[i].w1;
		if(v==fa)continue;
		dfs(v,u);
		key[u]+=key[v];
		if(key[v]){
			LL t=min(dp[v][0]+2*w0,dp[v][2]+2*w1);
			
			dp[u][0]+=dp[v][0]+2*w0;
			dp[u][2]+=t;
			d1=min(d1,dp[v][1]-dp[v][0]-w0);
			d3=min(d3,dp[v][3]+w0+w1-t);
			d4=min(d4,min(dp[v][4]+w1,dp[v][1]+w0)-t);
			
			LL ret1=dp[v][3]+w0+w1-t;
			if(ret1<fi1)se1=fi1,fi1=ret1,x=v;
			else se1=min(se1,ret1);
			
			LL ret2=dp[v][1]+w0-t;
			if(ret2<fi2)se2=fi2,fi2=ret2,y=v;
			else se2=min(se2,ret2);
		}
	}
	if(x&&y){
		if(x!=y)d4=min(d4,fi1+fi2);
		else d4=min(d4,min(fi1+se2,fi2+se1));
	}
	dp[u][1]=dp[u][0]+d1;
	dp[u][3]=dp[u][2]+d3;
	dp[u][4]=dp[u][2]+d4;
}

void read_and_parse(){	
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int x,y,w0,w1;
		scanf("%d%d%d%d",&x,&y,&w0,&w1);
		add_edge(x,y,w0,w1),add_edge(y,x,w0,w1);
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		int x;scanf("%d",&x);
		key[x]++;
	}
}
void solve(){
	dfs(1,0);
	printf("%lld
",dp[1][4]);
}
int main(){
	read_and_parse();
	solve();
	return 0;
} 
原文地址:https://www.cnblogs.com/wzj-xhjbk/p/10998425.html