题意
传送门 Codeforces gym103389J 最大权边独立集
题解
从边的角度考虑,会涉及两个节点的枚举,不易处理。仅考虑新增的 j ( 0 ≤ j ≤ k , 2 × j ≤ n ) j(0\leq j\leq k,2\times j\leq n) j(0≤j≤k,2×j≤n) 条边出现在最大权边独立集的情况,问题等价于选择图中 2 × j 2\times j 2×j 个节点删除的最大边权独立集。
d p [ v ] [ i ] [ j ] dp[v][i][j] dp[v][i][j] 代表以节点 v v v 为根的子树上,删除 i i i 个节点, v v v 状态为 j j j 时,子树的最大边权独立集。其中 j = 0 j=0 j=0 代表 v v v 未被删除或不属于独立集上某条边的端点;反之亦然。
对于形如 d p [ v ] [ i + j ] = ∑ d p [ v ] [ i ] + d p [ u ] [ j ] dp[v][i+j] = \sum dp[v][i] + dp[u][j] dp[v][i+j]=∑dp[v][i]+dp[u][j] 的树形背包问题,背包容量取 min ( s i z e v , k × 2 ) \min (size_v, k\times 2) min(sizev,k×2) 情况下(前者为子树节点数)时间复杂度 O ( n k ) O(nk) O(nk)。
DP 时将根节点看作第一颗子树,实现会简洁一些。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int MAXN = 1e5 + 5, MAXK = 105;
int N, K, P;
struct edge
{
int to, cost;
};
vector<edge> G[MAXN];
ll dp[MAXN][MAXK * 2][2], tmp[MAXK * 2][2];
int sz[MAXN];
void _max(ll &x, ll y) { x = max(x, y); }
void dfs(int v, int p)
{
sz[v] = 1;
for (int i = 0; i <= K * 2; ++i)
dp[v][i][0] = dp[v][i][1] = -1;
dp[v][0][0] = dp[v][1][1] = 0;
for (auto &e : G[v])
if (e.to != p)
{
int u = e.to;
dfs(u, v);
int a = min(sz[v], K * 2), b = min(sz[u], K * 2);
sz[v] += sz[u];
int c = min(sz[v], K * 2);
for (int i = 0; i <= c; ++i)
for (int j = 0; j < 2; ++j)
tmp[i][j] = -1;
for (int i = a; i >= 0; --i)
for (int j = 0; j <= b && j + i <= c; ++j)
{
if (dp[v][i][0] != -1)
{
for (int k = 0; k < 2; ++k)
if (dp[u][j][k] != -1)
_max(tmp[i + j][0], dp[v][i][0] + dp[u][j][k]);
}
if (dp[v][i][1] != -1)
{
for (int k = 0; k < 2; ++k)
if (dp[u][j][k] != -1)
_max(tmp[i + j][1], dp[v][i][1] + dp[u][j][k]);
}
if (dp[v][i][0] != -1 && dp[u][j][0] != -1)
_max(tmp[i + j][1], dp[v][i][0] + dp[u][j][0] + e.cost);
}
for (int i = 0; i <= c; ++i)
for (int j = 0; j < 2; ++j)
dp[v][i][j] = tmp[i][j];
}
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> N >> K >> P;
for (int i = 1; i < N; ++i)
{
int u, v, w;
cin >> u >> v >> w;
--u, --v;
G[u].push_back({v, w});
G[v].push_back({u, w});
}
dfs(0, -1);
ll res = 0;
for (int k = 0; k <= K && 2 * k <= N; k++)
{
if (dp[0][k * 2][0] != -1)
_max(res, (ll)k * P + dp[0][k * 2][0]);
if (dp[0][k * 2][1] != -1)
_max(res, (ll)k * P + dp[0][k * 2][1]);
}
cout << res << '\n';
return 0;
}
439

被折叠的 条评论
为什么被折叠?



