【树形DP】POJ 1947 Rebuilding Roads

通道:http://poj.org/problem?id=1947

题意:给出n,p,一共有n个节点,要求减去最少的边是多少,剩下p个节点

思路: dp[u][i]:记录u结点,要得到一棵i个节点的子树去掉的最少边数

 考虑其儿子k
 1)如果不去掉k子树,则 dp[u][i] = min(dp[u][j]+dp[k][i-j])  0 <= j <= i

 2)如果去掉k子树,则 dp[u][i] =  dp[u][i]+1 ,所以最后为: dp[u][i] = min (min(dp[u][j]+dp[k][i-j]) ,  dp[u][i]+1 );

答案最后就是在dp[i][m]中取小,要注意的一点是,如果i不是根,值还需要+1,因为要脱离原来的根,还要去掉一条边

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int const INF = 0x3fffffff;
int const MAX = 155;
 
struct Edge
{
    int to, next;
}e[MAX * MAX / 2];
 
int n, m;
int head[MAX], cnt, root;
int dp[MAX][MAX];
 
void Add(int x, int y) {
    e[cnt].to = y;
    e[cnt].next = head[x];
    head[x] = cnt++;
}
 
void DFS(int u, int fa) {
    for(int i = 0; i <= m; i++) dp[u][i] = INF;               
    dp[u][1] = 0;              
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].to;  
        if (v == fa) continue;
        DFS(v, u);
        for(int j = m; j >= 1; j--) {
            for(int k = 0; k < j; k++) {
                if(k) dp[u][j] = min(dp[u][j], dp[u][j - k] + dp[v][k]);
                else dp[u][j] = dp[u][j] + 1;
            }
        }
    }
}
 
int main() {
    scanf("%d %d", &n, &m);
    cnt = 0;
    memset(head, -1, sizeof(head));
    for(int i = 1; i < n; i++) {
        int x, y;
        scanf("%d %d", &x, &y);
        Add(x, y); Add(y, x);
    }
    DFS(1, -1);
    int ans = dp[1][m];
    for(int i = 1; i <= n; i++)    
        ans = min(ans, dp[i][m] + 1);
    printf("%d\n",ans);    
}
View Code

 

posted @ 2014-11-03 19:34  mithrilhan  阅读(157)  评论(0编辑  收藏  举报