[NOIP模拟] 拆网线 树形DP

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][01] 表示该节点是否与他的父亲节点连接时的最大独立边集,那么我们可以得到转移方程 :

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,那么我们还需多连 KDP[1][0]2 的边,如果小于那我们需要连 K/2+(Kand1) 边。


Code :

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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)