树形DP——Rebuilding Roads ( POJ 1947 )

  • 题目链接:
    http://poj.org/problem?id=1947

  • 分析:
    求最少删除多少边才能得到一颗含有P节点的子树。从根节点开始往下推能得出dp数组(见问题三)。

  • 题解:

1.用vector建树,存储:

for(int i=0;i<=n;i++)   
    vec[i].clear(); //每次先清空vector

for(int i=1;i<n;i++)
{
    int a,b;
    cin >> a >> b;
    vec[a].push_back(b);
    father[b]++; //记录每个节点的父节点个数,以便之后找出根节点
}

2.DFS搜索:

void DFS(int x)
{
    for(int i = 0; i<=p; i++)
        dp[x][i] = 10000000;//初始化
    dp[x][1] = 0;//选一个节点的情况不需要删边,赋值为0.
    for(int i=0;i<vec[x].size();i++)
    {
        int F = vec[x][i];
        DFS(F);//搜索每一个子节点
        for(int i = p;i>=1;i--)
        {
            int tem = dp[x][i]+1;//含有i个节点时,每次都不选子节点F的情况
            for(int j=1; j<i;j++)
            {
                tem = min(tem,dp[F][i-j]+dp[x][j]);//与每次选了子节点F然后F含有j个子节点的情况比较
            }
            dp[x][i]  = tem;
        }
    }
}
  • AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <cmath>
#define N 222
using namespace std;

int dp[N][N];
vector <int> vec[N];

int n, p, root;
void DFS(int x)
{
    for(int i = 0; i<=p; i++)
        dp[x][i] = 10000000;
    dp[x][1] = 0;
    for(int i=0;i<vec[x].size();i++)
    {
        int F = vec[x][i];
        DFS(F);
        for(int i =p;i>=1;i--)
        {
            int tem = dp[x][i]+1;
            for(int j=1; j<i;j++)
            {
                tem = min(tem,dp[F][i-j]+dp[x][j]);
            }
            dp[x][i]  = tem;
        }
    }
}

int solve()
{
    int ans;
    DFS(root);
    ans = dp[root][p];
    for(int i = 1; i<=n; i++)
        ans = min(ans, dp[i][p]+1);
    return ans;
}

int father[N];

int main()
{

    while(~scanf("%d%d",&n,&p))
    {
        memset(father, 0, sizeof(father));
        for(int i=0; i<=n; i++)
        {
            vec[i].clear();
        }
        for(int i=1;i<n;i++)
        {
            int a,b;
            cin >> a >> b;
            vec[a].push_back(b);
            father[b]++;
        }

        for(int i = 1; i<=n; i++)
        {
            if(!father[i])//找到根节点
            {
                root = i;
                break;
            }
        }
        printf("%d\n",solve());
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值