對於樹上狀態機dp問題的一些總結與思考
阿新 • • 發佈:2020-10-18
前言:全篇純屬個人理解與感悟,建議帶著批判的視角來審視。
本次部落格的一些有關例題(難度遞增)如下:AcWing285(沒有上司的舞會);AcWing323(戰略遊戲);AcWing1077(皇宮看守);洛谷P2279【HNOI2003】消防局的設立;
1.AcWing285(沒有上司的舞會)
題意:給你n個點n-1條邊,每個點有權值,一個邊最多可選一個點,問樹上最大可選權值和
題解:我們仔細審題會發現題目要求,"一個邊最多可選一個點"說明該邊上的點可選數量為0-1這兩種情況。因為是樹上問題,我們對於父親結點u來考慮。樹上狀態機dp問題無非是父親結點與孩子結點之間關係的狀態轉移。如果選了父親結點u(即定義狀態為dp[u][1]
AC程式碼:
#include<bits/stdc++.h> #pragma GCC optimize(2) #defineView Codell long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl '\n' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=1e5+5; int tot,head[maxn]; struct E{ int to,next; }edge[maxn<<1]; void add(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } int n,w[maxn],fa[maxn],root; int dp[maxn][2]; void dfs(int x){ dp[x][1]=w[x]; for(int i=head[x];i!=-1;i=edge[i].next){ int v=edge[i].to; dfs(v); dp[x][0]+=max(dp[v][0],dp[v][1]); dp[x][1]+=dp[v][0]; } } int main(){ scanf("%d",&n);mem(head,-1); rep(i,1,n) scanf("%d",&w[i]); rep(i,1,n-1){ int u,v;scanf("%d%d",&u,&v); add(v,u);fa[u]=v; } root=1; while(fa[root]) root=fa[root]; dfs(root); cout<<max(dp[root][0],dp[root][1])<<endl; }
2.AcWing323(戰略遊戲)
題意:給你n個點(編號為0~n-1),每條邊至少選1個點,問最少能選幾個點?
題解:感覺和上面那個題很類似,狀態轉移也很好想。因為題目要求樹上最小點數。那麼我們對於一條邊分析,如果父親結點u沒選,那麼其孩子節點v必選,所以狀態轉移方程為dp[u][0]=Σdp[v][1]。如果父親結點u選了,那麼其孩子理論上可選可不選,但我們要求求最小點,而dp的思路是通過區域性的最小的優化轉移到整體,即以小見大,所以我們需要讓區域性最小,故轉移方程為dp[u][1]=Σmin(dp[v][0],dp[v][1]).
AC程式碼:
#include<bits/stdc++.h> #pragma GCC optimize(2) #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl '\n' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=1500+5; int tot,head[maxn]; struct E{ int to,next; }edge[maxn<<1]; void add(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } int n,fa[maxn]; int dp[maxn][2]; void dfs(int x){ dp[x][1]=1; dp[x][0]=0; for(int i=head[x];i!=-1;i=edge[i].next){ int v=edge[i].to; dfs(v); dp[x][0]+=dp[v][1]; dp[x][1]+=min(dp[v][1],dp[v][0]); } } int main(){ while(~scanf("%d",&n)){ mem(head,-1);mem(fa,0);mem(dp,0);tot=0; rep(i,1,n){ int x,t;scanf("%d:(%d)",&x,&t); ++x; rep(i,1,t){ int qwq;scanf("%d",&qwq); ++qwq; fa[qwq]=x;add(x,qwq); } } int root=1; while(fa[root]) root=fa[root]; dfs(root); printf("%d\n",min(dp[root][1],dp[root][0])); } }View Code