[树形DP] HDU 4340

解决一个关于两人占领国家的问题,通过树形DP算法找到最小总花费。问题涉及两支军队占领城市并考虑城市间的相邻关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Capturing a country

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1321    Accepted Submission(s): 542


Problem Description
Ant and Bob two army want to capture a country. The country is consist of N cities. To capture the city i, it takes Ant A[i] minutes, and Bob needs B[i] minutes to capture city i. Due to the similarity of neighboring cities, If the city i and j are neighboring cities, if Ant has captured city i, then the time for Ant to capture city j is A[j]/2. Of course if Ant has captured city j, then the time for Ant to capture city i is A[i]/2. It is the same for Bob. We define the total time to capture a country be the time to capture city 1 + the time to capture city 2 + ... + the time to capture city N. Now we want to know the minimal total time.
For simplicity, we assume that there is only one path to go from one city to another city.
 

Input
The first line contains a integer N(0<N<100), which is the number of cities.Then following N lines describe A[1], A[2], …, A[N];Then following N lines describe B[1], B[2], …, B[N];Next comes N-1 lines, each contains two integers x, y, meaning that city x and city y are neighboring.
 

Output
Just output the minimal total time in a single line.
 

Sample Input
3 1 2 5 3 8 1 1 2 1 3
 

Sample Output
3
 


题意:

有一棵树,两个人 a 和 b 要去占领这棵树上的所有点,两个人占领每个点所需的花费各不相同

如果 a 占领了 i 点,那么去占领 与 i 相邻的点的时候,该点的花费减半,b 也是如此

问占领完整棵树所需最小花费

思路:

把这棵树分成几棵小的子树,每个子树只让同一个人占领

但是怎么操作呢?  用树形DP

建好树之后,从根节点往下深搜,搜到叶子节点之后开始回溯记录值

sa:表示该点取 a 的时候,且未确定起点,下面的子树的最小花费

sb:表示该点取 b 的时候,且未确定起点,下面的子树的最小花费

da:表示该点取 a 的时候,让取值最小的起点额外花费(即子树中 a 起点值的一半)

db:表示该点取 a 的时候,让取值最小的起点额外花费(即子树钟 b 起点值的一半)

dp[i][0][0]:表示 i 点取 a 且 尚未确定起点的最小花费

dp[i][0][1]:表示 i 点取 a 且 已经确定起点的最小花费

dp[i][1][0]:表示 i 点取 b 且 尚未确定起点的最小花费

dp[i][1][1]:表示 i 点取 b 且 已经确定起点的最小花费

那么:到达根节点之后 dp[i][0][0] =  sa + a[i] / 2 ; 都是取一半价值

dp[i][0][1] =  min(sa + a[i] , sa + a[i] / 2 + da) 该点为起点或者下面的某个点为起点

b的情况也是同上

每个点回溯后更新 sa sb

sa += min(dp[v][0][0],dp[v][1][1])  如果该点取 a ,那么就加等于下面a未确定起点的

或者是 下面 b 已经确定起点的 ,取小的那个值

更新 da db

da = min(da,dp[v][0][1] - min(dp[v][0][0],dp[v][1][1]))

   取当前点为a,且已确定起点的值,减去下面的子树为 a 为确定 或为 b已确定的 两个值中小的那个(因为小的那个才是构成当前 取a 且已确定的最优情况下的子树值)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
#define ll long long
#define mem(a,x) memset(a,x,sizeof(a))
#define maxn 105
const int inf = 1 << 30;

int n,a[maxn],b[maxn],dp[maxn][2][2];
vector<int>vec[maxn];

// dp[i][0][0]:表示 i 点取 a 且 尚未确定起点的最小花费

// dp[i][0][1]:表示 i 点取 a 且 已经确定起点的最小花费

// dp[i][1][0]:表示 i 点取 b 且 尚未确定起点的最小花费

// dp[i][1][1]:表示 i 点取 b 且 已经确定起点的最小花费  

void tree_dp(int x,int pre){
	int sz = vec[x].size();
	int sa,sb,da,db;
	sa = sb = 0;
	da = db = inf;
	for(int i = 0;i < sz;i++){
		int v = vec[x][i];
		if(v == pre)
			continue;
		tree_dp(v,x);
		sa += min(dp[v][0][0],dp[v][1][1]);		// 表示该点取 a 的时候,且未确定起点,下面的子树的最小花费
		sb += min(dp[v][1][0],dp[v][0][1]);	
		da = min(da,dp[v][0][1] - min(dp[v][0][0],dp[v][1][1]));	//更新预计起点的额外值
		db = min(db,dp[v][1][1] - min(dp[v][1][0],dp[v][0][1]));	
	}
	dp[x][0][0] = sa + a[x] / 2;	// 该点取半值,未确定起点,全部取半值
	dp[x][0][1] = min(sa + a[x],sa  + a[x] / 2 + da); 	// 该点做起点 或者 该点取半值,起点取下面的别的点
	dp[x][1][0] = sb + b[x] / 2;
	dp[x][1][1] = min(sb + b[x],sb + b[x] / 2 + db);
}

void init(){
	for(int i = 0;i <= n;i++){
		vec[i].clear();
	}
	mem(dp,0);
}

int main(){
	int u,v;
	while(scanf("%d",&n) != EOF){
		init();
		for(int i = 1;i <= n;i++){
			scanf("%d",&a[i]);
		}
		for(int i = 1;i <= n;i++){
			scanf("%d",&b[i]);
		}
		for(int i = 1;i < n;i++){
			scanf("%d %d",&u,&v);
			vec[u].push_back(v);
			vec[v].push_back(u);
		}
		tree_dp(1,0);
		int ans = min(dp[1][0][1],dp[1][1][1]);
		printf("%d\n",ans);
	}
		
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值