【POJ 1848】Tree【树形DP】

题意:给定一棵树,要添加一些边,不能是重边,不能是自环,让它的每一个结点都恰好属于一个环,求最少添加的边数。
思路:树形DP, 定义dp[i][0]表示i为根的子树所满足条件的最优解,dp[i][1]表示以i为根的子树除了i节点外的点满足条件的最优解,dp[i][2]表示以i为根的子树,除了i节点以及它的一个直接孩子节点除外的点满足条件的最优解。
转移方程:dp[i][1] = ∑dp[k][0] (k为i的孩子节点),
                    dp[i][2] = (∑dp[k][0])-dp[j][0]+min(dp[j][1], dp[j][2]),
                    dp[i][0] =  min(min((∑dp[k][0])-dp[j][0]+dp[j][2]+1), min((∑dp[k][0])-dp[j][0]-dp[h][0]+min(dp[j][1], dp[j][2])+min(dp[h][1], dp[h][2]))
第一个转移方程显然成立,第二个转移方程相当于在i的孩子中找到j使得i, j不在环中,也就是对于j为根的子树中,j不在环中所需要的边数最小值,也就是min(dp[j][1], dp[j][2])。
第三个转移方程,分为两种情况,在i的孩子中找到一个j并且使得j的一个孩子与j都不在换中,然后这三个点连成环,即min((∑dp[k][0])-dp[j][0]+dp[j][2]+1), 第二种情况就是寻找i的两个孩子,使得这三个点在同一个环中,也就是后面的式子了。
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define N 110
#define inf 10000

vector<int>V[N];
int dp[N][3];

void dfs(int u, int f) {
    int i, j, v, sum = 0;
    vector<int>sun;
    for (i = 0;i < V[u].size();i++) {
        v = V[u][i];
        if (v == f) continue;
        dfs(v, u);
        sun.push_back(v);
        sum += dp[v][0];
    }
    if (sun.size() == 0) {
        dp[u][1] = 0, dp[u][0] = inf, dp[u][2] = inf;
        return;
    }
    dp[u][1] = sum;
    int t1, t2;
    t2 = t1 = inf;
    for (i = 0;i < sun.size();i++) {
        v = sun[i];
        t2 = min(t2, sum-dp[v][0]+min(dp[v][1], dp[v][2]));
        t1 = min(t1, sum-dp[v][0]+dp[v][2]+1);
        for (j = i+1;j < sun.size();j++) {
            int vv = sun[j];
            t1 = min(t1, sum-dp[v][0]-dp[vv][0]+1+min(dp[v][1],dp[v][2])+min(dp[vv][1], dp[vv][2]));
        }
    }
    dp[u][0] = t1, dp[u][2] = t2;
}

int main() {
    int n, i, j, u, v;
    while (~scanf("%d", &n)) {
        for (i = 1;i <= n;i++) V[i].clear();
        for (i = 1;i < n;i++) {
            scanf("%d%d", &u, &v);
            V[u].push_back(v), V[v].push_back(u);
        }
        dfs(1, -1);
        if (dp[1][0] >= inf) puts("-1");
        else printf("%d\n", dp[1][0]);
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值