POJ 1947 Rebuliding Roads 树形DP

来源:互联网 发布:招聘数据分析报告 编辑:程序博客网 时间:2024/05/26 20:22

题意:给出一棵树,问至少删除几条边,才能出现大小为P的子树。
思路:删边类型的树形DP。
为了保持连通性,我们定义状态dp[u][j]为以u为根的子树,节点u必须选,得到大小为j的子树所删除的最少的边。因为无法直接将删边的信息汇总到根节点上,我们最后枚举节点,求出最后的值。
下面考虑状态转移:
对于节点u的儿子节点v,我们有两种选择:
1.不删除u,v之间的边,这个时候

dp[u][j]=min(dp[v][k]+dp[u][jk]),1kj 

2.删除u,v,之间的边,则
dp[u][j]=dp[u][j]+1 

j的范围:1jP 
边界条件:
dp[u][1]=0 

最终的答案我们要枚举节点u,同时还要注意,对于不是根结点的节点,我们还要断开该节点和父亲节点的边。
代码如下:

#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int MAX = 200;int N,P;int to[MAX<<1], nxt[MAX<<1],head[MAX],tot;int dp[MAX][MAX];void init(){    memset(head,-1,sizeof(head));    memset(dp,0x3f,sizeof(dp));    tot = 0;}void addedge(int u, int v){    to[tot] = v , nxt[tot] = head[u];    head[u] = tot++;}void dfs(int u, int p){    dp[u][1] = 0;    for(int i = head[u]; ~i; i = nxt[i]){        int v = to[i];        if(v == p) continue;        dfs(v,u);        for(int j = P; j >= 1; --j){            dp[u][j] += 1;            for(int k = 0; k < j; --k)                dp[u][j] = min(dp[u][j],dp[v][k] + dp[u][j-k]);        }    }}int main(void){    //freopen("input.txt","r",stdin);    init();    scanf("%d%d",&N,&P);    for(int i = 0; i < N - 1; ++i){        int u, v;        scanf("%d%d",&u,&v);        addedge(u,v);        addedge(v,u);    }    dfs(1,0);    int ans = dp[1][P];    for(int i = 2; i <= N; ++i)        ans = min(ans,dp[i][P] + 1);    printf("%d\n",ans);    return 0;}
0 0
原创粉丝点击