Tree Coverage DP Model
The tree coverage dynamic programming model addresses optimization problems where nodes are selected on a tree structure. Each chosen node can cover all nodes within a specified distance, with the goal of solving various optimization tasks (counting problems are not applicable).
State Definition and Transition
Let f[i][j] represent the optimal value after processing the subtree rooted at node i, where node i is either uncovered or specifically covered by node j.
Let best[i] store the optimal solution for node i.
The transition involves enumerating which node covers the current node u, then adjusting for overcounted costs. The recurrence relation becomes:
f[u][i] += max(best[v], f[v][i] + s - w[i])
This approach effectively reassigns the cost contribution of node i to the root node u. While the transition may appear discontinuous, the optimal substructure property ensures that all necessary states are captured within the best array.
Implementation Example
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1005;
const ll INF = 1e18;
ll dist[MAXN][MAXN], cost[MAXN], dp[MAXN][MAXN], optimal[MAXN];
ll n, multiplier, radius, penalty;
vector<pair<int,int>> adjacency[MAXN];
void computeDistances(int node, int parent, int root) {
for(auto& edge : adjacency[node]) {
if(edge.first != parent) {
dist[root][edge.first] = dist[root][node] + edge.second;
computeDistances(edge.first, node, root);
}
}
}
void treeDP(int node, int parent) {
for(auto& edge : adjacency[node]) {
if(edge.first != parent) {
treeDP(edge.first, node);
}
}
for(int i = 1; i <= n; ++i) {
dp[node][i] = (dist[node][i] <= radius ? cost[node] : 0) - penalty;
for(auto& edge : adjacency[node]) {
if(edge.first != parent) {
dp[node][i] += max(optimal[edge.first], dp[edge.first][i] + penalty);
}
}
}
for(int i = 1; i <= n; ++i) {
optimal[node] = max(optimal[node], dp[node][i]);
}
}
int main() {
scanf("%lld%lld%lld%lld", &n, &multiplier, &radius, &penalty);
for(int i = 1; i <= n; ++i) {
scanf("%lld", &cost[i]);
cost[i] *= multiplier;
}
for(int i = 1; i < n; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
adjacency[u].push_back({v, w});
adjacency[v].push_back({u, w});
}
for(int i = 1; i <= n; ++i) {
computeDistances(i, 0, i);
}
treeDP(1, 0);
printf("%lld\n", optimal[1]);
return 0;
}
Correctness Analysis
The optimal array serves a crucial role in maintaining solution validity. The state definition enforces that each node is either uncovered or explicitly covered by a specific node. This constraint prevents invalid configurations where a parent node is assigned to a distant cover while its child is optimally covered by a closer node. Although such assignments might initially seem problematic, the optimal substructure guarantees that any invalid state will have a superior valid alternative already represented in the optimal values.