洛谷 P1273 有线电视网
树上分组背包
即在树上做背包问题
对每棵子树做背包,再用子树的状态递推父亲的状态
第一次做树上背包,所以定义状态的时候想了很久
首先,节点号作为一个状态,同时为了实现状态转移第二个状态为用户数量
一开始想到费用上去了i表示节点号,x表示选取用户数,dp[i][x]表示最高费用
状态转移方程
i:节点 x:该节点已保证用户数 y:该子树提供用户数 s:子树节点 w:该路径费用
![]()
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
typedef long long LL;
const int maxn = 3005;
const int inf = 1e9;
void io() { //取消cin同步性
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
}
int n, m;
int dp[maxn][maxn];
vector<pii> E[maxn];
int dfs(int x) {
if (x > n - m) return 1; //叶子
int res = 0; dp[x][0] = 0; //res表示当前节点已接受用户数
for (int i = 0; i < E[x].size(); i++) {
int s = dfs(E[x][i].first);
for (int j = res; j >= 0; j--) {
for (int k = s; k >= 1; k--) { //动态转移方程
dp[x][j + k] = max(dp[x][j + k], dp[x][j] + dp[E[x][i].first][k] - E[x][i].second);
}
}
res += s;
}
return res;
}
int main() {
io();
cin >> n >> m;
for (int i = 1; i <= n; i++) //初始化
for (int j = 1; j <= n; j++)
dp[i][j] = -inf;
int k, a, c;
for (int i = 1; i <= n - m; i++) {
cin >> k;
while (k--){
cin >> a >> c; //存储路径
E[i].push_back(pii(a, c));
}
}
for (int i = n - m + 1; i <= n; i++) {
cin >> dp[i][1]; dp[i][0] = 0; //初始化叶子
}
dfs(1);
for (int i = m; i >= 0; i--) {
if (dp[1][i] >= 0) {
printf("%d\n", i);
return 0;
}
}
}