POJ 1947 Rebuilding Roads 树形dp+01背包

本文介绍了一个树形动态规划问题的解决方法,旨在通过递归地定义子问题来减少计算复杂度。具体而言,我们使用dp数组记录以每个节点为根时,保留特定数量节点所需的最小边删减数。
题意:

给了一棵树,树上有n个结点,然后让求最少删去多少条边,可以使剩下的一棵子树中存在p个结点。

思路:

dp[x][ j ]  表示 把x结点当成根时,形成的恰好含有j个结点的子树,至少删除的边数。

此时考虑递推式;

对于x结点 的dp[x][j] 他的状态可以由子结点推出来,

对于孩子结点son来说;

1)当不需要son时  dp[x][j]= dp[x][j]+1;   ///需要删去x与son的边

2)当需要son时 dp[x][ j ]= dp[ x ][ k ]+dp[ son ][ j-k ];

所以对两种情况取最优即可;

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <ctime>
#include <map>
#include <queue>
#define LL long long
#define INF 0x3f3f3f3f
#define bug puts("***************")
using namespace std;
const int N =200;
vector<int>vec[N];
bool vis[N];
int dp[N][N];
int father[N];
int n,m;
void DFS(int x){
    dp[x][1]=0;
    int len=vec[x].size();
    for(int i=0;i<len;i++){
        int son=vec[x][i];
        DFS(son);
        for(int j=m;j>=0;j--){        ///最多被用一次,所以倒着来,01背包,正着来就不对了。
            int tmp=dp[x][j]+1;
            for(int k=0;k<j;k++){
                tmp=min(tmp,dp[x][k]+dp[son][j-k]);
            }
            dp[x][j]=tmp;
        }
    }
}
int main(){
    memset(vis,0,sizeof(vis));
    memset(dp,INF,sizeof(dp));
    memset(father,0,sizeof(father));
    int u,v;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n-1;i++){
        scanf("%d%d",&u,&v);
        father[v]=1;
        vec[u].push_back(v);
    }
    int root;
    for(int i=1;i<=n;i++)
    if(!father[i]){
        root=i;
        break;
    }
    DFS(root);
    int ans=dp[root][m];
    for(int i=1;i<=n;i++){                ///除了根结点,其他结点作为子树的根,需要+1 ,因为要与原来的树切断
        ans=min(ans,dp[i][m]+1);
    }
    printf("%d\n",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值