1自底向上
树形dp以子树为最优解
自底向上的求
如求子树大小
代码
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> g[100005];
void dfs(int u,int p){
sz[u] = 1;
for(int i = 0;i < g[u].size();i++){
int v = g[u][i];
if(v == p)continue;
dfs(v,u);
sz[u] += sz[v];
}
}
int main(){
cin>>n;
for(int i = 1;i <= n-1;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
return 0;
}
例题
A. 战略游戏
题目描述
Bob 喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的方法。现在他有个问题。
现在他有座古城堡,古城堡的路形成一棵树。他要在这棵树的节点上放置最少数目的士兵,使得这些士兵能够瞭望到所有的路。
注意:某个士兵在一个节点上时,与该节点相连的所有边都将能被瞭望到。
请你编一个程序,给定一棵树,帮 Bob 计算出他最少要放置的士兵数。
输入格式
输入数据表示一棵树,描述如下。
第一行一个数 ,表示树中节点的数目。
第二到第 行,每行描述每个节点信息,依次为该节点编号 ,数值 , 表示后面有 条边与节点 相连,接下来 个数,分别是每条边的所连节点编号 。
对于一个有 个节点的树,节点标号在 到 之间,且在输入文件中每条边仅出现一次。
输出格式
输出仅包含一个数,为所求的最少士兵数。
样例
样例输入复制
4
0 1 1
1 2 2 3
2 0
3 0
样例输出复制
1
AC代码
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> g[100005];
int dp[100005][2];
void dfs(int u,int p){
dp[u][0] = 0,dp[u][1] = 1;
for(int i = 0;i < g[u].size();i++){
int v = g[u][i];
if(v == p)continue;
dfs(v,u);
dp[u][0] += dp[v][1];
dp[u][1] += min(dp[v][0],dp[v][1]);
}
}
int res[100005];
int main(){
cin>>n;
for(int i = 1;i <= n;i++){
int u,k,v;
cin>>u>>k;
while(k--){
cin>>v;
g[u].push_back(v);
}
}
dfs(0,0);
cout<<min(dp[0][0],dp[0][1]);
return 0;
}
背包树上
普通背包的改版
#include<bits/stdc++.h>
using namespace std;
vector<int> g[100005];
int f[1005][1005];//f(i,j)表示以i为节点选j门课
int sz[100005];
int n,m;
int a[1005];
int res[100005];
void F(int u){
f[u][1] = a[u];
sz[u] = 1;
for(int k = 0;k < g[u].size();k++){
int v = g[u][k];
F(v);
sz[u]+=sz[v];
for(int i = min(m,sz[u]);i >= 1;i--){
for(int j = 0;j < i&&j <= sz[v];j++){
f[u][i] = max(f[u][i],f[u][i-j]+f[v][j]);
}
}
}
}
int main(){
cin>>n>>m;
m++;
for(int i = 1;i <= n;i++){
int u;
cin>>u>>a[i];
g[u].push_back(i);
}
F(0);
cout<<f[0][m];
return 0;
}
换根DP
核心思路:以原始版本 来 求出变化
#include<bits/stdc++.h>
using namespace std;
vector<int> g[1000005];
long long dp[1000005];
long long sz[1000005];
int n,m;
void dfs(int u,int p){
dp[u] = 0;
sz[u] = 1;
for(int v:g[u]){
if(v == p)continue;
dfs(v,u);
sz[u] +=sz[v];
dp[u] += dp[v] + sz[v];
}
}
void dfs2(int u,int p){
for(int v:g[u]){
if(v == p)continue;
dp[v] += (dp[u]-(dp[v]+sz[v])+n -sz[v]);
dfs2(v,u);
}
}
int main(){
cin>>n;
for(int i = 1;i <= n-1;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,1);
dfs2(1,1);
long long id,mm = 0;
for(int i = 1;i <= n;i++){
if(mm < dp[i])mm = dp[i],id = i;
}
cout<<id;
return 0;
}
例题2 没有上司的舞会
题目描述
某大学有 �n 个职员,编号为 1…�1…n。
他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。
现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 ��ri,但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。
所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入格式
输入的第一行是一个整数 �n。
第 22 到第 (�+1)(n+1) 行,每行一个整数,第 (�+1)(i+1) 行的整数表示 �i 号职员的快乐指数 ��ri。
第 (�+2)(n+2) 到第 2�2n 行,每行输入一对整数 �,�l,k,代表 �k 是 �l 的直接上司。
输出格式
输出一行一个整数代表最大的快乐指数。
输入输出样例
输入 #1复制
7 1 1 1 1 1 1 1 1 3 2 3 6 4 7 4 4 5 3 5
输出 #1复制
5
说明/提示
数据规模与约定
对于 100%100% 的数据,保证 1≤�≤6×1031≤n≤6×103,−128≤��≤127−128≤ri≤127,1≤�,�≤�1≤l,k≤n,且给出的关系一定是一棵树。
AC代码
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> g[100005];
int dp[100005][2];//0不去,1去
int r[100005];
void dfs(int u,int p){
dp[u][0] = 0,dp[u][1] = r[u];
for(int i = 0;i < g[u].size();i++){
int v = g[u][i];
if(v == p)continue;
dfs(v,u);
dp[u][0] +=max(dp[v][0],dp[v][1]);
dp[u][1] += dp[v][0];
}
}
int res[100005];
int t;
bool pd[100005];
int main(){
cin>>n;
for(int i = 1;i <= n;i++){
cin>>r[i];
}
dfs(0,0);
for(int i = 1;i <= n-1;i++){
int l,k;
cin>>l>>k;
g[k].push_back(l);
pd[l] = 1;
}
cin>>t>>t;
for(int i = 1;i <= n;i++){
if(!pd[i])t = i;
}
dfs(t,t);
cout<<max(dp[t][0],dp[t][1]);
return 0;
}
换根DP 2
# [USACO10MAR] Great Cow Gathering G
## 题目描述
Bessie 正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。
每个奶牛居住在 $N$ 个农场中的一个,这些农场由 $N-1$ 条道路连接,并且从任意一个农场都能够到达另外一个农场。道路 $i$ 连接农场 $A_i$ 和 $B_i$,长度为 $L_i$。集会可以在 $N$ 个农场中的任意一个举行。另外,每个牛棚中居住着 $C_i$ 只奶牛。
在选择集会的地点的时候,Bessie 希望最大化方便的程度(也就是最小化不方便程度)。比如选择第 $X$ 个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和(比如,农场 $i$ 到达农场 $X$ 的距离是 $20$,那么总路程就是 $C_i\times 20$)。帮助 Bessie 找出最方便的地点来举行大集会。
## 输入格式
第一行一个整数 $N$ 。
第二到 $N+1$ 行:第 $i+1$ 行有一个整数 $C_i$。
第 $N+2$ 行到 $2N$ 行:第 $i+N+1$ 行为 $3$ 个整数:$A_i,B_i$ 和 $L_i$。
## 输出格式
一行一个整数,表示最小的不方便值。
## 样例 #1
### 样例输入 #1
```
5
1
1
0
0
2
1 3 1
2 3 2
3 4 3
4 5 3
```
### 样例输出 #1
```
15
```
## 提示
$1\leq N\leq 10^5$,$1\leq A_i\leq B_i\leq N$,$0 \leq C_i,L_i \leq 10^3$。
AC代码
#include<bits/stdc++.h>
using namespace std;
struct kkk {
int v,l;
};
vector<kkk> g[1000005];
long long dp[1000005];
long long sz[1000005];
int n,m;
long long c[1000005];
void dfs(int u,int p){
dp[u] = 0;
sz[u] = c[u];
for(kkk way:g[u]){
int v = way.v,l = way.l;
if(v == p)continue;
dfs(way.v,u);
dp[u]+=dp[v]+sz[v]*l;
sz[u]+=sz[v];
}
}
void dfs2(int u,int p){
for(kkk way:g[u]){
int v = way.v,l = way.l;
if(v == p)continue;
dp[v] +=(dp[u]-(dp[v]+sz[v]*l)+(sz[1]-sz[v])*l);
dfs2(way.v,u);
}
}
int main(){
cin>>n;
for(int i = 1;i <= n;i++){
cin>>c[i];
}
for(int i = 1;i <= n-1;i++){
int u,v,l;
cin>>u>>v>>l;
g[u].push_back(kkk{v,l});
g[v].push_back(kkk{u,l});
}
dfs(1,1);
dfs2(1,1);
long long id = 1e16,mm = 0;
for(int i = 1;i <= n;i++){
id = min(dp[i],id);
}
cout<<id;
return 0;
}
换根例题3
题目描述
W 市的交通规划出现了重大问题,市政府下定决心在全市各大交通路口安排疏导员来疏导密集的车流。但由于人员不足,W 市市长决定只在最需要安排人员的路口安排人员。
具体来说,W 市的交通网络十分简单,由 个交叉路口和 条街道构成,交叉路口路口编号依次为 。任意一条街道连接两个交叉路口,且任意两个交叉路口间都存在一条路径互相连接。
经过长期调查,结果显示,如果一个交叉路口位于 W 市交通网最长路径上,那么这个路口必定拥挤不堪。所谓最长路径,定义为某条路径 ,路径经过的路口各不相同,且城市中不存在长度大于 的路径,因此最长路径可能不唯一。因此 W 市市长想知道哪些路口位于城市交通网的最长路径上。
输入格式
第一行一个整数 ;
之后 行每行两个整数 ,表示 和 的路口间存在着一条街道。
输出格式
输出包括若干行,每行包括一个整数——某个位于最长路径上的路口编号。为了确保解唯一,请将所有最长路径上的路口编号按编号顺序由小到大依次输出。
样例
样例输入复制
10
0 1
0 2
0 4
0 6
0 7
1 3
2 5
4 8
6 9
样例输出复制
0
1
2
3
4
5
6
8
9
AC代码
#include<bits/stdc++.h>
using namespace std;
vector<int> g[1000005];
long long dp1[1000005],dp2[1000005];
long long sz[1000005];
int n,m;
void dfs(int u,int p){
for(int v:g[u]){
if(v == p)continue;
dfs(v,u);
if(dp1[v]+1 > dp1[u])dp2[u] = dp1[u],dp1[u] =dp1[v]+1;
else if(dp1[v]+1 > dp2[u])dp2[u] = dp1[v]+1;
}
}
void dfs2(int u,int p){
for(int v:g[u]){
int tmp;
if(v == p)continue;
if(dp1[v]+1 != dp1[u])tmp = dp1[u];
else tmp = dp2[u];
if(tmp+1 >dp1[v])dp2[v] = dp1[v],dp1[v] = tmp+1;
else if(tmp+1 > dp2[v])dp2[v] = tmp+1;
dfs2(v,u);
}
}
int main(){
cin>>n;
for(int i = 1;i <= n-1;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(0,0);
dfs2(0,0);
long long id,mm = 0;
for(int i = 0;i <= n;i++){
mm = max(mm,dp1[i]+dp2[i]);
}
for(int i = 0;i <= n;i++){
if(mm == dp1[i]+dp2[i])cout<<i<<endl;
}
return 0;
}
(完)