洛谷P3177 [HAOI2015]树上染色
\(\large{咕咕咕}\\\)
\(\large{题目链接}\)
\(\\\)
\(\Large\textbf{Solution: } \large{首先如果直接计算,由于k个点并不固定且还要计算两两间的距离,不好统计答案。\\最直接设f[i][j]表示以i为根的树中选j个黑点的最优答案,但是发现无法转移,\\因为全局最优解和局部最优没什么关系,于是考虑对贡献去做DP。\\考虑用状态f[i][j]表示i为根的树中选j个黑点对答案的贡献是多少。转移就容易了,\\每条边对答案的贡献即是 两边黑点的数量与边权之积 + 两边白点数量与边权之积。}\)
\(\\\)
\(\Large\textbf{Summary: } \large{1.当问题不满足最优子结构时,可以考虑DP每个子问题对答案的贡献。\\2.很多时候刷表法难以滚动数组,需要转化成填表法。}\)
\(\\\)
\(\Large\textbf{Code: }\)
#include <bits/stdc++.h>
#define gc() getchar()
#define LL long long
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
using namespace std;
const int N = 2e3 + 5;
int n, k, cnt, head[N], size[N];
LL f[N][N];
struct Edge {
int to, next, val;
}e[N << 1];
inline int read() {
int x = 0, flg = 1;
char ch = gc();
while (!isdigit(ch)) {
if (ch == '-') flg = -1;
ch = gc();
}
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
return x * flg;
}
inline void add(int x, int y, int w) {
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].val = w;
head[x] = cnt;
}
inline void dfs(int x, int fa) {
size[x] = 1;
f[x][0] = f[x][1] = 0;
for (int i = head[x]; i ; i = e[i].next) {
int u = e[i].to;
if (u == fa) continue;
dfs(u, x);
size[x] += size[u];
}
for (int i = head[x]; i ; i = e[i].next) {
int u = e[i].to;
if (u == fa) continue;
for(int j = min(k, size[x]); j >= 0; --j)
for(int p = 0; p <= min(j, size[u]); ++p) {
if (f[x][j - p] != - 1) {
LL val = 1LL * p * (k - p) * e[i].val + 1LL * (size[u] - p) * (n - k - size[u] + p) * e[i].val;
f[x][j] = max(f[x][j], f[x][j - p] + val + f[u][p]);
}
}
}
}
int main() {
n = read(), k = read();
int x, y, w;
rep(i, 2, n) x = read(), y = read(), w = read(), add(x, y, w), add(y, x ,w);
memset(f, -1, sizeof (f));
dfs(1, 0);
printf("%lld\n", f[1][k]);
return 0;
}