原题链接:https://ac.nowcoder.com/acm/problem/24953
题目大意:
求覆盖一个树所有的点,需要覆盖最小的次数,覆盖一个点需要耗费一次次数,覆盖完以后,那么相邻的点也被覆盖。
解题思路:
每一个点有三种状态:
1.自己覆盖自己
2.儿子节点覆盖自己
3.父亲节点覆盖自己
第一种情况找到儿子节点三种状态的最小值。
第三种情况找到儿子节点前两种状态的最小值。
第三种情况需要考虑自己被哪个节点覆盖,首先将他所有儿子节点的前两种状态的最小值相加,同时记录以这种方法有没有取到被儿子节点覆盖的情况,有直接将总和复制给第三种情况,否则取一个差值最小的将这个儿子节点取到在赋值。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pi; typedef complex <double> cp; #define debug(a) cout<<#a<<":"<<a<<endl; #define fr freopen("in.txt","r",stdin); #define Fill(x,a) memset(x,a,sizeof(x)) #define cpy(a,b) memcpy(a,b,sizeof(a)) const double PI = acos(-1); const int INF=0x3f3f3f3f; const int N=1e6+7; const int mod=1e9+7; int maxn,minn; int T,n,m,q; int head[N]; int cnt; int dp[N][3]; //0 自身 1 儿子 2 父亲 struct aa{ int u,next; }edge[N]; void init(){ Fill(head,0); cnt=1; return ; } void add(int u,int v){ edge[cnt].u=v,edge[cnt].next=head[u]; head[u]=cnt++; return ; } void dfs(int u,int p){ int v,ans=0; int a,b,flag; flag=INF; if(edge[head[u]].next==0&&u!=1){ //叶子节点 dp[u][0]=1; dp[u][1]=INF; dp[u][2]=0; return ; } dp[u][0]=1; for(int i=head[u];i!=0;i=edge[i].next){ v=edge[i].u; if(v==p) continue; dfs(v,u); dp[u][0]+=min(dp[v][0],min(dp[v][1],dp[v][2])); dp[u][2]+=min(dp[v][1],dp[v][0]); ans+=min(dp[v][0],dp[v][1]); if(dp[v][0]-dp[v][1]<flag){ flag=dp[v][0]-dp[v][1]; a=dp[v][0],b=dp[v][1]; } } if(flag>0){ ans=ans-b+a; } dp[u][1]=ans; return ; } int main(){ int u,v,ans; cin>>n; init(); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(1,0); ans=min(dp[1][0],dp[1][1]); cout<<ans<<endl; return 0; }