hiho challenge 15 C题
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\n",min(dp[1][2],dp[1][0]));
}
return 0;
}

浙公网安备 33010602011771号