Codeforces 219D - Choosing Capital for Treeland - [树形DP]

题目链接:https://vjudge.net/problem/CodeForces-219D

首先,类似于网上大部分题解所说的,设某一条边正向的weight=0,反向的weight=1,因为反向意味着我们(按DFS方向)走到这条边的时候,就得把这条边翻转,就相当于记一次(w=1)。

然后同样的,

dp[i][0]:表示以i为root的子树中,我们从i出发,要翻转(inverse)多少条边。

dp[i][1]:表示从i往其父亲方向走,要翻转(inverse)多少条边。

那么,最后我们要的,当一个点i为capital时,就要翻转dp[i][0]+dp[i][1]条边。

类似于HDU2196(http://www.cnblogs.com/dilthey/p/7186556.html),我们也要分两次做DFS,第一次做出所有的dp[i][0],第二次做出所有的dp[i][1];

状态转移方程:

对于某个节点u,有子节点(直接相连的)v1~vk,则

dp[u][0] = ( edge[u][v1].w + dp[v1][0] ) + …… + ( edge[u][vk].w + dp[vk][0] )

对于某个节点v,有父亲节点u,则

dp[v][1] = dp[u][1] + ( dp[u][0] - ( edge[u][v].w + dp[v][0] )  ) + ( (edge[u][v].w==1) ? 0 : 1 )

(这个状态转移方程画个图就比较好想,一个节点往上走,可以是它的父亲节点再往上走,也可以是它的父亲节点的其他孩子树,再包括它和它父亲节点相连的那条边)

所以,代码如下:

 1 #include<cstdio>
 2 #include<vector>
 3 #define MAXN 200000+5
 4 using namespace std;
 5 int n,dp[MAXN][2];
 6 struct Edge{
 7     int u,v,w;
 8 };
 9 vector<Edge> adj[MAXN];
10 void dfs1(int now,int par)
11 {
12     if(adj[now].empty())
13     {
14         dp[now][0]=0;
15         return;
16     }
17     for(int i=0;i<adj[now].size();i++)
18     {
19         Edge edge=adj[now][i];
20         int next=edge.v;
21         if(next==par) continue;
22         dfs1(next,now);
23         dp[now][0]+=edge.w+dp[next][0];
24     }
25 }
26 void dfs2(int now,int par)
27 {
28     if(now==1) dp[now][1]=0;
29     for(int i=0;i<adj[now].size();i++)
30     {
31         Edge edge=adj[now][i];
32         int next=edge.v;
33         if(next==par) continue;
34         dp[next][1] = dp[now][1] + ( dp[now][0] - (edge.w+dp[next][0]) ) + (edge.w?0:1);
35         dfs2(next,now);
36     }
37 }
38 int main()
39 {
40     scanf("%d",&n);
41     for(int i=1;i<n;i++)
42     {
43         int from,to;
44         scanf("%d%d",&from,&to);
45         adj[from].push_back((Edge){from,to,0});
46         adj[to].push_back((Edge){to,from,1});
47     }
48     dfs1(1,0);
49     dfs2(1,0);
50     vector<int> ans;
51     int min=dp[1][0]+dp[1][1];
52     for(int i=1;i<=n;i++)
53     {
54         if(min>dp[i][0]+dp[i][1])
55         {
56             min=dp[i][0]+dp[i][1];
57             ans.clear();
58         }
59         if(min==dp[i][0]+dp[i][1])
60         {
61             ans.push_back(i);
62         }
63     }
64     printf("%d\n",min);
65     for(int i=0;i<ans.size();i++)
66     {
67         if(i!=0) printf(" ");
68         printf("%d",ans[i]);
69     }
70     printf("\n");
71 }

可以说是第一道大部分都是自己独立完成的树形DP,还是值得庆祝一下的~

posted @ 2017-07-17 00:03  Dilthey  阅读(381)  评论(0)    收藏  举报