首页 > 代码库 > codeforces 219D 树形dp

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\n" , 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("\n");72     }73     return 0;74 }

 

codeforces 219D 树形dp