• codeforces 219D 树形dp


    题目大意:

    根据n个城市,设置n-1条有向边,希望设定一个中心城市,能通过修改一些道路的方向到达任何一座城市,希望修改的道路数量最少

    输出这个最少的道路数量,并且把所有符合的可作为中心的城市编号一个个输出来

    开始也实在想不出,根据树形dp,首先要确定一棵树,可是这里的边乱七八糟的,没法确定根

    后来看了别人的思路,原来还是自己太死脑筋了,根本不需要确定根,可以把任意一个城市作为根来解决题目,这里假定1为根

    把所有的有向边看作无向边,但要记录那条边是真实存在的,哪条是自己加的

    用dp[i]表示 i 点到达其对应的子树中的所有点需要修改的边的数量

    用一次dfs得到dp[i]

    然后网上的大牛们都说2次dfs,后一次因为没写清楚,自己也不想看代码,就自己想了,可就是没办法写出dfs,最后发现直接就可以循环做。。。而且更好理解

    做法:

    len[i]记录 i 到 root 这里定义root为 1 长度,也就是i 到 1 之间有多少条边

    change[i] 表示这 len[i]条边中有多少条改变了

    这两个值可以在第一次dfs中实现

    然后对于每一个点 i 来说,其实为了让 i 能够到达任何一点,只要改动 i 到 1 这len[i]条边即可,画个图很容易发现

    1作为树根,i 到 1 的过程经过的都是一棵棵子树的根,能到达这些子树的根,必能到达子树中的任何一点,所以希望能够到达 1 即可

    用rec[i]记录以 i 为中心城市需要改动的边

    rec[i] = dp[1] - change[i] + (len[i] - change[i]) //减第一个change[i]是把原来改过的边改回来,然后+后面的是因为为了 i 到1,需要将那些

    方向正常的 len[i] - change[i]倒转

    最后通过rec[i]得到答案即可= =

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 using namespace std;
     5 const int N = 200005;
     6 
     7 int first[N] , k , len[N] , change[N] , dp[N] , rec[N];
     8 
     9 struct Edge{
    10     int y , next;
    11     bool flag;
    12 }e[N<<1];
    13 
    14 void add_edge(int x , int y , bool flag)
    15 {
    16     e[k].y = y , e[k].next = first[x] , e[k].flag = flag;
    17     first[x] = k++;
    18 }
    19 
    20 void dfs(int u , int fa)
    21 {
    22     for(int i = first[u] ; i!=-1 ; i=e[i].next){
    23         int v = e[i].y;
    24         if(v == fa) continue;
    25         len[v] = len[u]+1;
    26         change[v] = change[u];
    27         if(!e[i].flag) change[v]++;
    28         dfs(v , u);
    29         if(!e[i].flag){
    30             dp[u] += dp[v]+1;
    31         }
    32         else{
    33             dp[u] += dp[v];
    34         }
    35     }
    36 }
    37 
    38 int main()
    39 {
    40    // freopen("a.in" , "r" , stdin);
    41     int n , a , b;
    42     while(scanf("%d" , &n) == 1)
    43     {
    44         memset(first , -1 , sizeof(first));
    45         k = 0;
    46         for(int i=1 ; i<n ; i++){
    47             scanf("%d%d" , &a , &b);
    48             add_edge(a , b , true);
    49             add_edge(b , a , false);
    50         }
    51         memset(dp , 0 , sizeof(dp));
    52         len[1] = 0 , change[1] = 0;
    53         dfs(1 , -1);
    54 
    55         int minn = 200000000;
    56 
    57         for(int i=1 ; i<=n ; i++){
    58             rec[i] = dp[1] + len[i] - 2*change[i];
    59            // cout<<"i: "<<i<<" "<<len[i]<<" "<<change[i]<<" "<<rec[i]<<endl;
    60             minn = min(minn , rec[i]);
    61         }
    62         printf("%d
    " , minn);
    63         int num = 0;
    64         for(int i=1 ; i<=n ; i++){
    65             if(minn == rec[i]){
    66                 if(num == 0) printf("%d" , i);
    67                 else printf(" %d" , i);
    68                 num ++;
    69             }
    70         }
    71         printf("
    ");
    72     }
    73     return 0;
    74 }
  • 相关阅读:
    Linux下如何查看版本信息
    java单利模式设计
    MIT 2012 分布式课程基础源码解析-底层通讯实现
    MIT 2012分布式课程基础源码解析-事件管理封装
    MIT 2012分布式课程基础源码解析-线程池实现
    MIT 2012分布式课程基础源码解析一-源码概述
    Leetcode按Tag刷题
    网页搜集系统
    c/c++中的各种字符串转换
    gentoo装X服务器时显卡选择
  • 原文地址:https://www.cnblogs.com/CSU3901130321/p/4232633.html
Copyright © 2020-2023  润新知