题意:n个点,n-1条边组成一颗有根树,求这颗树的最小点覆盖。
思路:抛开树这个模型,将所有点置于二分图的两边,所有边都双向连接之后,做二分图最大匹配并将结果除于2就是答案。因为数据范围较大,所以需要使用Hopcroft_karp算法求解二分图最大匹配,使用匈牙利算法会被卡TLE。
C++代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 1510; const int inf = 0x3f3f3f3f; int tol; int head[maxn]; struct edge { int to,next; }es[maxn<<1]; void init() { tol = 0; memset ( head , -1 , sizeof(head) ); } void addedge( int u , int v ) { es[tol].to = v; es[tol].next = head[u]; head[u] = tol++; } int n,m,dis; int Mx[maxn],My[maxn]; int dx[maxn],dy[maxn]; bool used[maxn]; bool SearchP() { dis = inf; queue<int>Q; memset ( dx , -1 , sizeof(dx) ); memset ( dy , -1 , sizeof(dy) ); for ( int i=1 ; i<=n ; i++ ) { if ( Mx[i]==-1 ) { dx[i] = 0; Q.push(i); } } while ( !Q.empty() ) { int u = Q.front(); Q.pop(); if ( dx[u]>dis ) break; for ( int i=head[u] ; i!=-1 ; i=es[i].next ) { int v = es[i].to; if ( dy[v]==-1 ) { dy[v] = dx[u]+1; if ( My[v]==-1 ) { dis = dy[v]; } else { dx[My[v]] = dy[v]+1; Q.push(My[v]); } } } } return dis!=inf; } bool dfs( int u ) { for ( int i=head[u] ; i!=-1 ; i=es[i].next ) { int v = es[i].to; if ( !used[v]&&dy[v]==dx[u]+1 ) { used[v] = true; if ( My[v]!=-1&&dy[v]==dis ) continue; if ( My[v]==-1||dfs(My[v]) ) { My[v] = u; Mx[u] = v; return true; } } } return false; } int Hopcroft_karp() { int res = 0; memset ( Mx , -1 , sizeof(Mx) ); memset ( My , -1 , sizeof(My) ); while ( SearchP() ) { memset ( used , false , sizeof(used) ); for ( int i=1 ; i<=n ; i++ ) if ( Mx[i]==-1&&dfs(i) ) res++; } return res>>1; } int main() { while ( scanf ( "%d%d" , &n , &m )==2 ) { init(); for ( int i=1 ; i<n ; i++ ) { int u,v; scanf ( "%d%d" , &u , &v ); addedge ( u+1 , v+1 ); addedge ( v+1 , u+1 ); } printf ( "%d\n" , Hopcroft_karp() ); } return 0; }