[树形DP] HDU 4340

来源:互联网 发布:淘宝网开直通车的要求 编辑:程序博客网 时间:2024/05/22 00:25

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
31 2 53 8 11 21 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 105const 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;}