您的位置:首页 > 其它

[NOIP模拟] 拆网线 树形DP

2017-10-23 17:04 330 查看

Description

    给出一棵树,现去掉一些边,使树存在 K 个点,每点至少与其中一个点相连,求最小**边。

Input

    t 组数据,n 个点的树,K,以及相连的边。

Output

    答案。

Sample input

2

4 4

1 2 3

4 3

1 1 1

Sample output

2

2

Solution :

     我们定 DP[i][0∼1] 表示该节点是否与他的父亲节点连接时的最大独立边集,那么我们可以得到转移方程 :DP[i][0]=∑DP[j][0]+max(max(DP[j][1]−DP[j][0]),0)DP[i][1]=∑DP[j][0]

    那么这棵树的最大独立边集就为 DP[1][0], 如果 K 大于最大边集 * 2,那么我们还需多连 K−DP[1][0]∗2 的边,如果小于那我们需要连 K/2+(Kand1) 边。

Code :

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <ctime>
#include <map>
#include <vector>
using namespace std;

inline int read() {
int i = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') f = -1; ch = getchar();
}
while(isdigit(ch)) {
i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
}
return i * f;
}

const int MAXN = 1e5 + 5;
int dp[MAXN][2], first[MAXN], nxt[MAXN * 2], to[MAXN * 2], n, k, tot;

inline void addedge(int x, int y) {
nxt[++tot] = first[x]; first[x] = tot; to[tot] = y;
nxt[++tot] = first[y]; first[y] = tot; to[tot] = x;
}

inline void dfs(int x, int fa) {
dp[x][1] = 1; dp[x][0] = 0;
int mx = 0;
for(int i = first[x]; i; i = nxt[i]) {
if(to[i] != fa) {
dfs(to[i], x);
dp[x][1] += dp[to[i]][0];
dp[x][0] += dp[to[i]][0];
mx = max(dp[to[i]][1] - dp[to[i]][0], mx);
}
}
dp[x][0] += mx;
}

int main() {
int t = read();
while(t--) {
memset(first, 0, sizeof(first));
memset(nxt, 0, sizeof(nxt));
tot = 0;
n = read(), k = read();
for(int i = 1; i <= n - 1; ++i) addedge(read(), i + 1);
dfs(1, 1);
int now = dp[1][0];
if(now * 2 >= k) printf("%d\n", k / 2 + (k & 1));
else printf("%d\n", now + k - now * 2);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  NOIP模拟 树形DP DP