• poj 2057 && 1947


    两道树形 dp

    题目:http://poj.org/problem?id=2057

    题意:蜗牛的房子丢在了树上的某个节点上(每个节点的可能性相同),有些节点上有虫子,可以告诉蜗牛它的房子是不是在以这个节点为根的子树上,蜗牛找到房子的最小数学期望值是多少。

    首先期望是:找到房子走的步数 / 叶子节点的总数。叶子节点总数是固定的,那么走的步数越少,期望值也就越小,因为一个节点可能有多个孩子,从不同的孩子开始走得到的步数是不同的,优先选择哪个孩子这里用了贪心,解释见代码。定义三个数组

    su[N]  // su [i] 表示走到以 节点 i 为根的子树成功找到房子的步数 如果 i 是叶子节点那么 su [i] = 0

     fa[N] // fa [i] 表示走到以节点 i 为根的子树找不到房子的步数 如果 i 为叶子节点 或 worm[i] == 0 那么 fa[i] = 0

     le[N] // le [i] 表示 i 为根的子树 叶子节点的数目 初始化所有的节点该值为零,为了递归计算其他节点,所以当遇到叶子节点是 赋值 le [i] = 1;

    View Code
     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <iostream>
     4 #include <algorithm>
     5 #include <vector>
     6 #include <math.h>
     7 #define N 1010
     8 #define inf 100000000
     9 #define _clr(a,val) (memset(a,val,sizeof(a)))
    10 
    11 using namespace std;
    12 
    13 typedef long long ll;
    14 
    15 int su[N],fa[N],le[N];
    16 int worm[N];
    17 int tr[N][N];
    18 int n;
    19 bool cmp(int x,int y)
    20 {
    21     return (fa[x] + 2) * le[y] < (fa[y] + 2) * le[x];
    22 }
    23 void dp(int x)
    24 {
    25     int i,j;
    26     if(tr[x][0] == 0)  // x 为叶子节点
    27     {
    28         le[x] = 1;
    29         su[x] = fa[x] = 0;
    30         return ;
    31     }
    32     for(i = 1; i <= tr[x][0]; i++)
    33     dp(tr[x][i]); 
    34     for(i = 1; i <= tr[x][0]; i++)
    35     {
    36         le[x] += le[tr[x][i]]; // 求 以 x 节点为根的子树叶子节点数目
    37         if(worm[x] == 0)
    38         {
    39             fa[x] += (fa[tr[x][i]] + 2);  // 在树的最底层 或 该节点有虫子时 fa[i] = 0, 当不是最低是 转移方程 fa[父节点] = fa[子节点] + 2 
    40         }
    41     }
    42     int tem[N];  
    43     for(i = 0; i < tr[x][0]; i++) // 贪心
    44     {
    45         tem[i] = tr[x][i + 1];  
    46     }
    47     sort(tem,tem + tr[x][0],cmp);  // 排序
    48     for(i = 1,j = 0; i <= tr[x][0]; i++)
    49     {
    50         su[x] += ((j + 1) * le[tem[i - 1]] + su[tem[i - 1]]); // su的转移方程(这个方程的解释  http://blog.sina.com.cn/s/blog_5f5353cc0100hd08.html ),从这里可以看出,找到房子的步数 取决于 (fa[u] + 2) * le[v],所以贪心的时候按这个从小到大排序,然后依次选择
    51         j += (fa[tem[i - 1]] + 2);
    52     }
    53 }
    54 int main()
    55 {
    56     int i,x;
    57     char ch;
    58     //freopen("data.txt","r",stdin);
    59     while(scanf("%d",&n),n)
    60     {
    61         _clr(tr,0);
    62         for(i = 1; i <= n; i++)
    63         {
    64             worm[i] = 0;
    65             scanf("%d %c",&x,&ch);
    66             if(x != -1)
    67             {
    68                 tr[x][++tr[x][0]] = i; 
    69             }
    70             if(ch == 'Y')
    71             {
    72                 worm[i] = 1;
    73             }
    74             su[i] = fa[i] = le[i] = 0;
    75         }
    76         dp(1);
    77         double ans = su[1] * 1.0 / (le[1] * 1.0);
    78         printf("%.4lf\n",ans);
    79     }
    80     return 0;
    81 }

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

    题意:给一颗 n 个节点的树,问去掉最少的边可以得到一颗子树,而且该子树含有 p 个节点

    dp[i][j] 表示 在以 i 为根节点的子树中 j 个节点时 去掉的最少边数 转移方程   dp[x][j] = min(dp[x][j],dp[map[x][i]][k] + dp[x][j - k] - 2),关于方程里 - 2 的一种解释: 我们把以 x 为根的节点的子树,把每一个分支作为背包的物品,决策就是每一个分支的选与不选,而对于每一个分支的状态其实就是该问题的一个子问题,然后这样分割成 2 块后,我们会发现多砍了该节点与子节点的边两次,要减去之 。求解这个 dp[i][j] 用典型的 0 - 1背包形式去求

    View Code
     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <iostream>
     4 #include <algorithm>
     5 #include <vector>
     6 #include <math.h>
     7 #define N 160
     8 #define inf 100000000
     9 #define _clr(a,val) (memset(a,val,sizeof(a)))
    10 
    11 using namespace std;
    12 
    13 typedef long long ll;
    14 
    15 int fa[N];
    16 int dp[N][N];
    17 int map[N][N];
    18 int n,p;
    19 void dfs(int x)
    20 {
    21     int i,j,k;
    22     for(i = 1; i <= map[x][0]; i++)
    23     dfs(map[x][i]);
    24     for(i = 1; i <= map[x][0]; i++)
    25     {
    26         for(j = p; j > 1; j--)
    27         {
    28             for(k = 1; k < j; k++)
    29             dp[x][j] = min(dp[x][j],dp[map[x][i]][k] + dp[x][j - k] - 2);
    30         }
    31     }
    32 }
    33 int main()
    34 {
    35     int i,j;
    36     int x,y;
    37     //freopen("data.txt","r",stdin);
    38     while(scanf("%d%d",&n,&p) != EOF)
    39     {
    40         _clr(map,0);
    41         for(i = 0; i < N; i++)
    42         for(j = 0; j < N; j++)
    43         dp[i][j] = inf;
    44         _clr(fa,-1);
    45         for(i = 0; i < n - 1; i++)
    46         {
    47             scanf("%d%d",&x,&y);
    48             map[x][++map[x][0]] = y;
    49             fa[y] = x;
    50         }
    51         for(i = 1; i <= n; i++)
    52         dp[i][1] = map[i][0] + 1;
    53         for(i = 1; i <= n; i++)
    54         if(fa[i] == -1) break;
    55         dp[i][1] --;
    56         dfs(i);
    57         int ans = inf;
    58         for(i = 1; i <= n; i++)
    59         {
    60             ans = min(ans,dp[i][p]);
    61         }
    62         printf("%d\n",ans);
    63     }
    64     return 0;
    65 }

     

  • 相关阅读:
    mysql_fetch_row()获取显示数据
    数组上下移动
    croppie 在Angular8 中使用
    关于 element 的 backToTop
    苹果手机new Date()问题
    js精简代码集合
    vue 中使用高德地图, 地图选点
    代替if else 的表单验证方法!
    记一次webpack打包样式加载问题
    echarts 饼图的指示线(labelline) 问题
  • 原文地址:https://www.cnblogs.com/fxh19911107/p/2636481.html
Copyright © 2020-2023  润新知