DP题。状态很容易设,dp[u][0]表示u点子树解决,dp[u][1]表示剩一条链,dp[u][2]表示邻边全炸.
转移有点难,看代码解释:
#include <iostream> #include <cstdio> #include <algorithm> #include <vector> using namespace std; const int MAX=100005; vector<int>t[MAX]; int dp[MAX][3]; void dfs(int u,int f){ dp[u][0]=dp[u][1]=dp[u][2]=0; int sz=t[u].size(),v,counts=0,s=0; for(int i=0;i<sz;i++){ v=t[u][i]; if(v!=f){ dfs(v,u); } else continue; s+=min(min(dp[v][0],dp[v][1]),dp[v][2]); if(dp[v][2]>min(dp[v][0],dp[v][1])){////dp[v][2]>min(dp[v][0],dp[v][1])这种情况表示选择不是全炸的,否则全炸,因为全炸的话,就不需要再多一次操作了。除非不是 ///全炸的更少,因为如果更少,则dp[v][2]至少大1 counts++;///统计有多少个是儿子节点不是子邻边全炸的,这样每两个儿子节点就可以使用一次解决。 } dp[u][2]+=dp[v][0];///邻边全炸,肯定是要儿子节点的子树全部解决。 } dp[u][0]+=s+counts/2+((counts%2)?1:0);///如果有奇数个儿子节点不是邻边全炸,就/2后,需要一次和根节点的操作才可以全解决,偶数则不需要 dp[u][1]+=s+counts/2;////剩一条链也不需要加上一次操作 dp[u][2]++; } int main(){ int n,u,v; while(scanf("%d",&n)!=EOF){ for(int i=1;i<=n;i++) t[i].clear(); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); t[u].push_back(v); t[v].push_back(u); } dfs(1,0); printf("%d ",min(dp[1][2],dp[1][0])); } return 0; }