题解:P1273 有线电视网 - 树形背包DP
【题目传送门】
本题解是对洛谷题解https://www.luogu.com.cn/article/t5t4lx51的再阐述 & 学习笔记。
这里是这个 \(\mathcal {O(nm)}\) 的时间复杂度的做法:
我们看到题目不难想到写一个树形背包 DP,很显然这就是裸题。
我们设 \(f_{i,j}\) 为从当前节点 \(i\) 向下获得了 \(j\) 个用户收益的最大利润。那么答案很明显就是 \(\set{f_{i,j}\ge0}\) 之中最大的 \(j\)。
我们优先考虑按照后序遍历先预处理这棵树,给每个点按照后序遍历的编号。即搜索到一个节点的时候,优先搜索自述,给其中的每个点都编号完了以后,再给当前的点编号。
然后我们开始 DP,将所有 \(f_{i,j}\) 初始化为 \(-\inf\) ,对于所有 \(f_{i,0}\) 初始化为 \(0\)。
- 如果当前点 \(i\) 是叶子结点,那么不难得出:
\[\displaystyle \Large f_{i,j}=\max{f_{i-1,j-1}+c_i\ ,\ f_{i-1,j}}
\]
也就是我们选择或不选择当前的这个节点来创造收益。
- 如果当前点不是叶子节点,我们如果选择当前的节点的话就有:
\[\displaystyle \Large f_{i,j}=f_{i-1,j}+c_i
\]
- 如果不选择当前节点的话,那么我们得去他的子树里面找一个节点的状态更新上来,把点直接缩了(也就是下面的子树和现在这个节点一个都不选,所以直接覆盖上来是对的,这一步需要多思考一下),而且根据后序遍历,我们的子树编号在 \([\ i-siz_i\ ,\ i\ )\) ,这一点实际上和 DFS 是差不多一致的 不过我们的 DFS 序是 \((\ i\ ,\ i+siz_i\ ]\):
\[\displaystyle \Large f_{i,j}=f_{i-siz_i,j}
\]
所以有了最终的 DP 式子:
\[\displaystyle \Large{f_{i_j}=\max{f_{i_1,j}+c_i\ ,\ f_{i-siz_i,j}}}
\]
我们的时间复杂度很好看出来是 \(\mathcal {O(n\cdot m)}\) 的。
\(\mathcal{CODE}\)
#include<bits/stdc++.h>
using namespace std;
#define itn int
#define reaD read
#define int long long
inline void read(int &x){
x=0;
int p=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')p=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
x*=p;
}
void write(int x){
if(x<0){
putchar('-');
x*=-1;
}
if(x<10){
putchar(x+48);
return;
}
write(x/10);
putchar(x%10+48);
}
const int N=3100;
int n,m;
int c[N],siz[N],f[N][N],idx[N],tot;
vector<int>e[N];
void dfs(int u){
siz[u]=1;
for(int v:e[u]){
dfs(v);
siz[u]+=siz[v];
}
idx[++tot]=u;
}
main(void){
read(n);
read(m);
for(int u=1,k;u<=n-m;u++){
read(k);
for(int j=1,v,w;j<=k;j++){
read(v);
read(w);
c[v]=-w;
e[u].push_back(v);
}
}
for(int i=n-m+1,w;i<=n;i++){
read(w);
c[i]+=w;
}
dfs(1);
for(int i=0;i<=tot;i++){
for(int j=1;j<=m;j++){
f[i][j]=-1e9;
}
}
for(int i=1;i<=tot;i++){
int u=idx[i];
//i->the arr on tree;u-> real arr
for(int j=1;j<=m;j++){
if(n-m+1<=u)f[i][j]=max(f[i-1][j-1]+c[u],f[i-1][j]);
else f[i][j]=max(f[i-1][j]+c[u],f[i-siz[u]][j]);
}
}
for(int i=m;i>=0;i--){
if(f[tot][i]>=0){
write(i);
puts("");
exit(0);
}
}
}

浙公网安备 33010602011771号