樹形dp,是一種建立在樹形結構上的dp,因此dfs一般是實現它的通用手段。
是一種很美的動態規劃呢。
P1352 沒有上司的舞會
P1352 沒有上司的舞會。
在一棵樹中,找到若干個互相獨立(即互相沒有邊直接相連)的點,使它們的權值和最大。
我們發現,間隔選擇的方法(只選深度為奇數/偶數的點)是不可行的。一個很簡單的反例是這棵樹是一條鏈:10 <-> 3 <-> 3 <-> 10
,顯然選擇\(1,4\)才是正確的。
那麼我們該怎麼做呢?我們可以dfs遍歷這棵樹,對於一個節點,我們考慮兩種選擇情況:
- 選當前節點:那麼子節點就不能選。當前權值為所有子節點不選狀態下的答案和。
- 不選當前節點:那麼子節點可以選,也可以不選。當前答案為所有子節點兩種狀態下的最大值之和。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,happy[6010];
vector<int> ch[6010];
bool b[6010];
int mem[6010][2];
int dfs(int pos,bool is){
if(mem[pos][is]) return mem[pos][is];
int ans=0,len=ch[pos].size();
if(is){//如果上司去,則員工必須不去
for(int i=0;i<len;i++) ans+=dfs(ch[pos][i],0);
ans+=happy[pos]*(happy[pos]>0);
}else{//如果上司不去,則員工有兩種選擇
for(int i=0;i<len;i++) ans+=max(dfs(ch[pos][i],0),dfs(ch[pos][i],1));
}
return mem[pos][is]=ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>happy[i];
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
b[x]=1;
ch[y].push_back(x);
}
for(int i=1;i<=n;i++){
if(!b[i]){
cout<<max(dfs(i,0),dfs(i,1));
break;
}
}
return 0;
}
UVA1292 Strategic game
UVA1292 Strategic game
在一棵樹中,找到若干個點,每個點放置\(1\)個士兵,每個士兵可以看守所在點鄰接的所有邊,現在我們想知道:要看守這棵樹的所有邊,最少需要多少士兵。
對於每個節點,考慮其選擇情況:
- 該節點放士兵:那麼子節點可以放,也可以不放。取每個子節點放/不放的最小值求和即可。
- 該節點不放士兵:那麼子節點必須全放。取每個子節點放的和即可。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> G[1510];
int f[1510][2];
bool vis[1510];
void dfs(int pos){
f[pos][1]=1;
for(auto i:G[pos]){
dfs(i);
f[pos][0]+=f[i][1];//如果當前不選,那麼子節點必須全選
f[pos][1]+=min(f[i][0],f[i][1]);//如果當前選,則選最小
}
}
int main(){
while(~scanf("%d",&n)){
memset(f,0,sizeof f);
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<=n;i++){
int pos,m,num;
scanf("%d:(%d)",&pos,&m);
pos++;
for(int j=1;j<=m;j++){
cin>>num;
num++;
G[pos].push_back(num);
vis[num]=1;
}
}
int awa=-1;
for(int i=1;i<=n;i++){
if(!vis[i]){
awa=i;
break;
}
}
dfs(awa);
cout<<min(f[awa][0],f[awa][1])<<endl;
}
return 0;
}
P1122 最大子樹和
P1122 最大子樹和
在一棵樹中選擇一塊聯通分量,讓其點權和最大。
用\(f[i]\)表示以\(i\)為根節點子樹的答案。顯然它的值就是子節點答案中,非負答案的和。
最後遍歷\(f\)求出最大值即可。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,v[16010];
vector<int> G[16010];
bool vis[16010];
int f[16010];
void dfs(int pos){
vis[pos]=1;
f[pos]=v[pos];
for(auto i:G[pos]){
if(vis[i]) continue;
dfs(i);
if(f[i]>0) f[pos]+=f[i];
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>v[i];
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1);
int ans=INT_MIN;
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
cout<<ans;
return 0;
}