zoukankan      html  css  js  c++  java
  • POJ 1947 Rebuilding Roads【树状DP】


    POJ 1947 Rebuilding Roads

    http://poj.org/problem?id=1947
    大意:有n个点组成一个树,问至少要删除多少条边才能获得一棵有p个结点的子树?
    分析:树形DP
        满足上诉条件的子树必由某个结点和它的若干子树组成,
     1.用dp[i][j]表示结点i恰好保留j个子树时至少需要删除的边数,若结点i有子树s
       a.删除子树s,dp[i][j]+1
       b.子树s保留k个结点:dp[s][k-1]+dp[i][j-k]
       综上:dp[i][j] =min(dp[i][j]+1,dp[s][k-1]+dp[i][j-k])(0<k<=j)
      
     2.默认结点1为根DFS,获取dp[i][j]值
        3.遍历所有的节点
        a.若子树根为结点1,则需删除的边为dp[1][p-1]
        b.若子树的根为节点i(i!=1),则需删除的边为dp[i][p-1]+1;
       综上:ans  = min(dp[i][p-1]+1,dp[1][p-1])(1<i<=n)

    View Code
    1 #include<stdio.h>
    2 #include<string.h>
    3 constint N =150+10;
    4 struct node
    5 {
    6 int v;
    7 node *next;
    8 }edge[N*2],*index[N];
    9 int ednum;
    10 bool visited[N];
    11
    12 inline void addEdge(int a,int b)
    13 {
    14 node *p =&edge[ednum++];
    15 p->v = b;
    16 p->next = index[a];
    17 index[a]=p;
    18 }
    19
    20 int dp[N][N];//dp[i][j]表示子树i保留j个后代至少需要删除多少边
    21 int n,p;//n为原树节点个数,p为所求子树的节点数
    22
    23 inline int min(int a,int b)
    24 {
    25 return a<b?a:b;
    26 }
    27
    28 int dfs(int id)//求子树id的dp值,并返回原树中以id为根的子树包含的结点数目
    29 {
    30 if(visited[id]==true)return0;
    31 visited[id]=true;
    32
    33 int sonNum =0,i,j;//sonNum统计当前节点的后代数目
    34
    35 for(i=1;i<=n;i++)//初始化,dp[id][i] = n表示该状态不可达
    36 dp[id][i]=n;
    37
    38 dp[id][0]=0;//dp[id][0]表示未访问子结点前,即默认没有子节点的情况下0个孩子需要的删除0条边即可
    39
    40 for(node *p = index[id];p;p=p->next)
    41 {
    42 if(visited[p->v]==false)
    43 {
    44 int curSon=dfs(p->v);
    45 sonNum+=curSon;
    46
    47 for(i=sonNum;i>0;i--)//转化为01背包求解
    48 {
    49 int upper = curSon;
    50 if(upper>i)upper = i;
    51 int temp = dp[id][i]+1;
    52 for(j =1;j<=upper;j++)
    53 {
    54 temp = min(temp,dp[p->v][j-1]+dp[id][i-j]);
    55 }
    56 dp[id][i]=temp;
    57 }
    58 dp[id][0]++;
    59 }
    60 }
    61
    62 sonNum++;//计入本身
    63 return sonNum;
    64 }
    65
    66 int main()
    67 {
    68 while(scanf("%d%d",&n,&p)!=EOF)
    69 {
    70 int i;
    71 for(i=1;i<=n;i++)
    72 {
    73 index[i]=NULL;
    74 visited[i]=false;
    75 }
    76
    77 int a,b;
    78 ednum =0;
    79 for(i=1;i<n;i++)
    80 {
    81 scanf("%d%d",&a,&b);
    82 addEdge(a,b);
    83 addEdge(b,a);
    84 }
    85
    86 dfs(1);//计算dp值
    87 int ans = dp[1][p-1];
    88 for(i=2;i<=n;i++)
    89 {
    90 if(dp[i][p-1]+1<ans)ans = dp[i][p-1]+1;
    91 }
    92
    93 printf("%d\n",ans);
    94 }
    95 return0;
    96 }
  • 相关阅读:
    凸包模板
    F
    luogu P1220 关路灯
    [国家集训队]小Z的袜子
    [AHOI2009]维护序列
    luogu P3373 【模板】线段树 2
    [国家集训队]数颜色 / 维护队列
    luogu P3393 逃离僵尸岛
    [Usaco2005 Mar]Out of Hay 干草危机
    [USACO07NOV]牛栏Cow Hurdles
  • 原文地址:https://www.cnblogs.com/AndreMouche/p/1996102.html
Copyright © 2011-2022 走看看