codeforces219D(树形dp)

src:https://vjudge.net/problem/CodeForces-219Dnode

两次dfs的特色,状态的转移要用到父节点的状态,因此分支节点在第二次dfs中才能用到父节点,而根节点在第一次dfs中就算出dp值了,由于它没有父节点,因此它的值在第二次dfs中传下去!!!ios

树形dp通常解决的树上的最优解问题,第一个下标通常指处在的结点位置,接下来的下标通常是消耗的代价,或是依赖的什么个其余节点!!!spa

#include <iostream> #include<cstdlib> #include<algorithm> #include<cmath> #include<functional> #include<utility> #include<string> #include<string.h> #include<vector> #include<iomanip> #include<stack>
using namespace std; #define FOR(i,a,b) for(int i=a;i<=b;i++)
#define Max(a,b) a=max(a,b)
#define Min(a,b) a=min(a,b)
const int inf=99999999; int n,dp[200005],mi,cnt,head[200005]; int k[200005],kcnt; struct node { int to,v,ne; }edge[400010]; void init() { cnt=0;kcnt=0; memset(dp,0,sizeof(dp)); mi=inf; memset(head,-1,sizeof(head)); } void add_edge(int a,int b,int v){edge[cnt].to=b;edge[cnt].v=v;edge[cnt].ne=head[a];head[a]=cnt++;} void dfs(int rt,int u,int fa,int dis) { dp[rt]+=dis; for(int i=head[u];i!=-1;i=edge[i].ne){ int v=edge[i].to; if(v==fa)continue; dfs(rt,v,u,edge[i].v); } } void dfs1(int u,int fa) { for(int i=head[u];i!=-1;i=edge[i].ne){ int v=edge[i].to; if(v==fa)continue; dfs1(v,u); dp[u]+=(dp[v]+edge[i].v); } } void dfs2(int u,int fa) { for(int i=head[u];i!=-1;i=edge[i].ne){ int v=edge[i].to; if(v==fa)continue; dp[v]+=(dp[u]-dp[v]-edge[i].v+edge[i^1].v);//灵活运用异或求反向边(已经add了的,并且cnt从0开始~)
 dfs2(v,u); } } int main() { ios::sync_with_stdio(false); while(cin>>n){ int a,b; init(); for(int i=1;i<n;i++){cin>>a>>b;add_edge(a,b,0);add_edge(b,a,1);} //FOR(i,1,n){dfs(i,i,-1,0);} //用dfs求路程和的复杂度O(n2)超时!!!
        dfs1(1,-1);dfs2(1,-1); //树形dp O(2n)~~~ //FOR(i,1,n)cout<<dp[i]<<' ';cout<<endl;//         FOR(i,1,n){ if(dp[i]<mi){ cnt=0; mi=dp[i]; k[cnt++]=i; } else if(dp[i]==mi){k[cnt++]=i;} } cout<<mi<<endl; cout<<k[0]; FOR(i,1,cnt-1)cout<<' '<<k[i]; cout<<endl; } return 0; }
相关文章
相关标签/搜索