HDU 4340 Capturing a country 树形DP

题目大意:

就是现在又A和B两种颜色, 给一棵树涂色, 每个点u有两种花费A[u]和B[u]代表直接涂上两种颜色分别的花费, 但如果和这点的相连的点被涂色了, 那么吧这个点涂上和相连那个点一样的颜色的话, 花费降至直接涂的一半, 求将这棵树都涂色的最小花费


大致思路:

就是一个树形DP...转移方程什么的看代码注释吧...


代码如下:

Result  :  Accepted     Memory  :  1624 KB     Time  :  0 ms

/*
 * Author: Gatevin
 * Created Time:  2015/8/2 21:20:04
 * File Name: Sakura_Chiyo.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

/*
 * 用dp[u][i][j]表示当前在编号为u的点, 这个点选择图为颜色i, 这个点和其子树所在的这一片连通区域是否选择了起点
 * 用v表示u的儿子
 * 那么不难发现对于dp[u][0][0]其状态只能来自于dp[v][1][1]和dp[v][0][0] (不能来自dp[v][0][1], 因为这样会使得连通处有突破口(起点))
 * 对于dp[u][0][1]可以发现起点要么来自这个点u, 这个花费是A[now] + ∑min(dp[v][1][1], dp[v][0][0])
 * 要么是以其根节点所在子树为起点, 我是枚举起点是v1那么这个话费是 A[now]/2 + dp[v1][0][1] + ∑min(dp[v2][1][1], dp[v2][0][0])
 * 于是去上面两个的最小就好了, 其实后来看了别人的做法发现可以不用枚举v1, 直接取
 * A[now] / 2 + ∑min(dp[v][1][1], dp[v][0][0]) + (dp[v1][0][1] - min(dp[v1][1][1], dp[v1][0][0]))最小的
 * 也就是去dp[v][0][1] - min(dp[v][1][1], dp[v][0][0])最大的那个v就可以了这样逼我的复杂度低一个级别...
 * 不过这个题N = 100也就没什么了...
 * 对于dp[u][1][1]和dp[u][1][0]就和dp[u][0][1], dp[u][0][0]对应一个道理了
 * 最后结果就是min(dp[1][0][1], dp[1][1][1]) (1作为根)
 */

#define maxn 110

int N;
int A[maxn], B[maxn];
int dp[maxn][2][2];
vector<int> G[maxn];

void dfs(int now, int fa)
{
    int nex, nex2;
    for(int i = 0, sz = G[now].size(); i < sz; i++) if((nex = G[now][i]) != fa)
        dfs(nex, now);
    if(G[now].size() == 1 && fa != -1)
    {
        dp[now][0][0] = A[now] / 2;
        dp[now][0][1] = A[now];
        dp[now][1][0] = B[now] / 2;
        dp[now][1][1] = B[now];
        return;
    }
    dp[now][0][0] = A[now] / 2;
    int selectNow = A[now];
    int selectSon = 1e9;
    for(int i = 0, sz = G[now].size(); i < sz; i++) if((nex = G[now][i]) != fa)
    {
        dp[now][0][0] += min(dp[nex][0][0], dp[nex][1][1]);
        selectNow += min(dp[nex][0][0], dp[nex][1][1]);
        int tmp = A[now] / 2 + dp[nex][0][1];
        for(int j = 0; j < sz; j++) if(i != j && (nex2 = G[now][j]) != fa)
            tmp += min(dp[nex2][1][1], dp[nex2][0][0]);
        selectSon = min(selectSon, tmp);
    }
    dp[now][0][1] = min(selectNow, selectSon);
    
    dp[now][1][0] = B[now]/2;
    selectNow = B[now];
    selectSon = 1e9;
    for(int i = 0, sz = G[now].size(); i < sz; i++) if((nex = G[now][i]) != fa)
    {
        dp[now][1][0] += min(dp[nex][1][0], dp[nex][0][1]);
        selectNow += min(dp[nex][0][1], dp[nex][1][0]);
        int tmp = B[now] / 2 + dp[nex][1][1];
        for(int j = 0; j < sz; j++) if(i != j && (nex2 = G[now][j]) != fa)
            tmp += min(dp[nex2][0][1], dp[nex2][1][0]);
        selectSon = min(selectSon, tmp);
    }
    dp[now][1][1] = min(selectNow, selectSon);
    return;
}

int main()
{
    while(~scanf("%d", &N))
    {
        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++) G[i].clear();
        int u, v;
        for(int i = 1; i < N; i++)
        {
            scanf("%d %d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1, -1);
        printf("%d\n", min(dp[1][1][1], dp[1][0][1]));
    }
    
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值