HDU 5758 Explorer Bo
思维,树形$dp$。
首先选择一个度不为$0$的节点作为根节点,将树无根转有根。
这题的突破口就是要求瞬间移动的次数最少。
次数最少,必然是一个叶子节点走到另一个叶子节点,然后瞬间移动一次,再从一个叶子节点走到另一个叶子节点,然后瞬间移动一次……
因为叶子节点总数可能是奇数,可能是偶数,那么接下来要分类讨论一下了。
叶子节点总数为偶数:
这种情况下,叶子节点可以两两配对。如果 下面叶子节点个数是偶数,那么$<u,v>$这条边通过的次数就是$2$;如果$v$下面叶子节点个数是偶数,那么$<u,v>$这条边通过的次数就是$1$次(画图就可以知道)。知道了每条边的通过次数,那么答案就是每条边通过次数的总和。
叶子节点总数为奇数:
这种情况下,与上面偶数的情况唯一不同的就是:有一个叶子节点无法配对。那么我们只需枚举哪一个叶子节点不配对即可。枚举从$root$开始,$dfs$一次就可以了。
这种题目虽然涉及到的知识量极少,但是思维量极高,对于这种题目弱渣表示暂时完全不能独立想出来。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<iostream> using namespace std; typedef long long LL; const double pi=acos(-1.0),eps=1e-8; void File() { freopen("D:\\in.txt","r",stdin); freopen("D:\\out.txt","w",stdout); } template <class T> inline void read(T &x) { char c = getchar(); x = 0;while(!isdigit(c)) c = getchar(); while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); } } const int maxn=100000+10; int T,n,ans; struct Edge{int u,v,nx;}e[2*maxn]; int root,h[maxn],sz; int f[maxn],r[maxn],d[maxn]; void add(int u,int v) { e[sz].u=u; e[sz].v=v; e[sz].nx=h[u]; h[u]=sz++; } void dfs(int x,int fa) { int sum=0; f[x]=d[x]=0; for(int i=h[x];i!=-1;i=e[i].nx) { if(fa==e[i].v) continue; sum++; dfs(e[i].v,x); f[x]=f[x]+f[e[i].v]; if(f[e[i].v]%2==0) d[x]=d[x]+2; else d[x]=d[x]+1; } if(sum==0) f[x]=1; } void Find(int x,int fa,int Ans) { for(int i=h[x];i!=-1;i=e[i].nx) { if(fa==e[i].v) continue; if(f[e[i].v]==1) { ans=min(ans,Ans); continue; } else { if(f[e[i].v]%2==0) Find(e[i].v,x,Ans-1); else Find(e[i].v,x,Ans+1); } } } int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); memset(h,-1,sizeof h); memset(r,sz=0,sizeof r); for(int i=1;i<=n-1;i++) { int u,v; scanf("%d%d",&u,&v); r[u]++; r[v]++; add(u,v); add(v,u); } if(n==2) { printf("1\n"); continue; } if(n==1) { printf("0\n"); continue; } for(int i=1;i<=n;i++) if(r[i]!=1) { root=i; break; } dfs(root,0); ans=0; for(int i=1;i<=n;i++) { if(i==root) continue; if(f[i]%2==0) ans=ans+2; else ans=ans+1; } if(f[root]%2==0) printf("%d\n",ans); else { Find(root,0,ans); printf("%d\n",ans); } } return 0; }