洛谷2899 手機網路(樹形DP)
阿新 • • 發佈:2018-11-11
傳送門
【題目分析】
N個點,N-1條邊,那麼這就是一棵樹,再看看這種父親與兒子之間的相互影響的關係,那麼就鎖定樹形DP做法。
當我們確定了一個點為根的時候,所有點的父子關係都確定了。
考慮一個點,有三種選擇:1.自己建 2.自己不建,由兒子將自己覆蓋 3.自己不建,由父親覆蓋,所以開dp[MAXN][3]記錄3個狀態的最優解。
考慮轉移。如果當前點選擇自己建,那麼他的兒子就有兩種選擇:1和3;
如果當前點選擇自己不建由兒子覆蓋,那麼他的兒子中就至少有一個要選擇建,所以遞迴處理兒子建和不建的最優解,再選擇哪些兒子要建。
如果當前點被父親覆蓋,那麼當前點就不用考慮,直接統計所有兒子最優解即可。
【程式碼~】
#include<bits/stdc++.h> using namespace std; const int MAXN=1e4+10; const int MAXM=2e4+10; const int INF=0x3f3f3f3f; int n,cnt; int head[MAXN]; int nxt[MAXM],to[MAXM]; int dp[MAXN][3],sum[MAXN]; int Read(){ int i=0,f=1; char c; for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar()); if(c=='-') f=-1,c=getchar(); for(;c>='0'&&c<='9';c=getchar()) i=(i<<3)+(i<<1)+c-'0'; return i*f; } void add(int x,int y){ nxt[cnt]=head[x]; head[x]=cnt; to[cnt]=y; cnt++; } int DP(int u,int fa,int stau){ if(dp[u][stau]!=-1) return dp[u][stau]; if(stau==0){ dp[u][stau]=1; for(int i=head[u];i!=-1;i=nxt[i]){ int v=to[i]; if(v==fa) continue; dp[u][stau]+=min(DP(v,u,0),DP(v,u,2)); } } if(stau==1){ dp[u][stau]=INF; int total=0; for(int i=head[u];i!=-1;i=nxt[i]){ int v=to[i]; if(v==fa) continue; sum[v]=min(DP(v,u,0),DP(v,u,1)); total+=sum[v]; } for(int i=head[u];i!=-1;i=nxt[i]){ int v=to[i]; if(v==fa) continue; dp[u][stau]=min(dp[u][stau],DP(v,u,0)+total-sum[v]); } } if(stau==2){ dp[u][stau]=0; for(int i=head[u];i!=-1;i=nxt[i]){ int v=to[i]; if(v==fa) continue; dp[u][stau]+=min(DP(v,u,0),DP(v,u,1)); } } return dp[u][stau]; } int main(){ memset(head,-1,sizeof(head)); memset(dp,-1,sizeof(dp)); n=Read(); for(int i=1;i<n;++i){ int x=Read(),y=Read(); add(x,y),add(y,x); } cout<<min(DP(1,-1,0),DP(1,-1,1)); return 0; }