题意:
给出一棵树,找出这样的点:这个点通过反转边的反向达到其他各个点,并且反转次数最少。同时要输出最少的反转次数。
题解:
两次搜索。dp[i][2] 0表示第一次搜索这个点要到其他点需反转的最小次数,1表示第二次。
状态转移:
if(val==0) dp[u][1]=dp[fa][1]+1;
else dp[u][1]=dp[fa][1]-1;
#include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> #include<math.h> #include<stdlib.h> #include<time.h> using namespace std; #define oo 0x3f3f3f3f #define maxn 200005 int dp[maxn][2]; struct EDGE { int v,val,next; }E[maxn<<1]; int head[maxn],tol; void inst() { memset(head,-1,sizeof head); tol=0; } void add_edge(int u,int v) { E[tol].v=v; E[tol].val=0; E[tol].next=head[u]; head[u]=tol++; E[tol].v=u; E[tol].val=1; E[tol].next=head[v]; head[v]=tol++; } void Down(int u,int fa) { dp[u][0]=0; for(int i=head[u];i!=-1;i=E[i].next) { int v=E[i].v; if(v==fa) continue; Down(v,u); dp[u][0]+=dp[v][0]+E[i].val; } } void Up(int u,int fa,int val) { if(u==fa) { dp[u][1]=dp[u][0]; } else { if(val==0) dp[u][1]=dp[fa][1]+1; else dp[u][1]=dp[fa][1]-1; } for(int i=head[u];i!=-1;i=E[i].next) if(E[i].v!=fa) Up(E[i].v,u,E[i].val); } int main() { int n,u,v; while(scanf("%d",&n)!=EOF) { inst(); for(int i=1;i<=n-1;i++) { scanf("%d %d",&u,&v); add_edge(u,v); } Down(1,-1); Up(1,1,0); int ans=oo,f=0; for(int i=1;i<=n;i++) ans=min(dp[i][1],ans); printf("%d\n",ans); for(int i=1;i<=n;i++) if(dp[i][1]==ans) { if(!f) f=1; else printf(" "); printf("%d",i); } puts(""); } return 0; } /* 3 2 1 2 3 4 1 4 2 4 3 4 */