您的位置:首页 > 其它

【POJ 1848】Tree【树形DP】

2014-08-04 21:35 302 查看
题意:给定一棵树,要添加一些边,不能是重边,不能是自环,让它的每一个结点都恰好属于一个环,求最少添加的边数。

思路:树形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
;
int dp
[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]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: