• POJ 1947 Rebuilding Roads


    题目大意:

    根据两个点建立一条有向边,最后可形成的是一棵树,希望通过切除一些边,使一棵含有p个节点的子树被独立出来,希望切除的边数最少,输出这个边数

    这个是第一次自己完完整整做出来的树形Dp题目,没有参考别人的DP思路,虽然自己很快想好了,但是总是无法合适的进行组织,做了很久,但自己做出来的

    总是会比取理解别人代码来的效果好很多

    用dp[u][j] 记录u号节点这棵子树中含有j个点需要砍去的最少的边数

    两次dfs , 第一次dfs查出对应节点的下方子树中一共有多少个节点,顺便记录一个节点在当前延伸出去了几条边

    也就是dp[u][1] ,表示最后u代表的子树下方全部砍去只剩u这一个点

    第二次dfs中自底向上不断将子树添加进来,根据dp值判断是否适合加入这个子树,如果适合加入,那么因为之前砍去的那条边要加回来,所以

    砍去的边要少一

    dp[u][j] = min(dp[u][j] , dp[v][k] + dp[u][j-k] - 1) k<j

    这里判断dp[u][j] , 和dp[v][k] + dp[u][j-k] - 1的大小,前一个表示没必要添加v子树,后一个表示添加了v子树效果更好

    实际这里的二重循环也是相当于类似背包问题的思想

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 
     5 using namespace std;
     6 const int INF = 0x3f3f3f3f;
     7 const int N = 205;
     8 
     9 int dp[N][N] , sum[N] , first[N] , col[N] , root , k;
    10 
    11 struct Edge{
    12     int y , next;
    13 }e[N];
    14 
    15 void add_edge(int x , int y)
    16 {
    17     e[k].y = y  , e[k].next = first[x];
    18     first[x] = k++;
    19 }
    20 
    21 void dfs1(int u)
    22 {
    23     sum[u] = 1;
    24     dp[u][1] = 0;
    25     for(int i=first[u] ; i!=-1 ; i=e[i].next){
    26         dp[u][1] ++;
    27         int v = e[i].y;
    28         dfs1(v);
    29         sum[u] += sum[v];
    30     }
    31 }
    32 
    33 void dfs2(int u , int n)
    34 {
    35     dp[u][sum[u]] = 0;
    36     for(int i=first[u] ; i!=-1 ; i=e[i].next){
    37         int v = e[i].y;
    38         dfs2(v , n);
    39         //这里j必须递减,因为运算中用到了dp[u][j-k],如果是递增顺序,那么会提前把要使用的更新了
    40         for(int j=sum[u] ; j>=2 ; j--){
    41             for(int k=j-1 ; k>=1 ; k--){
    42                 dp[u][j] = min(dp[u][j] , dp[v][k] + dp[u][j-k] - 1);
    43             }
    44         }
    45     }
    46 }
    47 
    48 int main()
    49 {
    50    // freopen("a.in" , "r" , stdin);
    51     int n , p , a , b;
    52     while(scanf("%d%d" , &n , &p) == 2)
    53     {
    54         memset(first , -1 , sizeof(first));
    55         memset(col , 0 , sizeof(col));
    56         k = 0;
    57         for(int i=1 ; i<n ; i++){
    58             scanf("%d%d" , &a , &b);
    59             add_edge(a , b);
    60             col[b] = 1;
    61         }
    62         for(int i = 1 ; i<=n ; i++)
    63             if(!col[i]) root = i;
    64 
    65         memset(dp , 0x3f , sizeof(dp));
    66         dfs1(root);
    67         dfs2(root , n);
    68 
    69         int minn = INF;
    70         for(int i=1 ; i<=n ; i++){
    71             if(i == root) minn = min(minn , dp[i][p]);
    72             else minn = min(minn , dp[i][p]+1);
    73         }
    74         printf("%d
    " , minn);
    75     }
    76     return 0;
    77 }
  • 相关阅读:
    极简代码搞定视频剪辑
    python 遍历本地文件
    安装Anaconda需要知道的pc信息
    ng4 路由多参数传参以及接收
    Js之设置日期时间 判断日期是否在范围内
    VsCode显示左边折叠代码+-按钮
    H5+.Net Webapi集成微信分享前后端代码 微信JS-SDK wx.onMenuShareTimeline wx.onMenuShareAppMessage
    压测工具之JMeter之环境配置及运行
    C# 交集、差集、并集、去重
    Nginx初学者指南
  • 原文地址:https://www.cnblogs.com/CSU3901130321/p/4232375.html
Copyright © 2020-2023  润新知