题解: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);
		}
	}
}
posted @ 2025-11-28 10:15  Noivelist  阅读(18)  评论(0)    收藏  举报