洛谷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;
}

猜你喜欢

转载自www.cnblogs.com/Miraclys/p/12576649.html