Jzoj P1046 [GDOI2005]寻宝之旅

xiaoxiao2021-02-28  59

题目大意:

N N 个点,每个点有一个价值AiAi。它们之间由 N1 N − 1 条边,边不会形成环,即两个宝藏点之间有且只有一条通路。探险队有 M M 个人,从点1出发,每次可以留一个人在此点开采宝藏,也可以不留,然后其余的人可以分成若干队向这一点相邻的点走去。 如果他们把队伍分成两队或两队以上,就必须留一个人在当前点,提供联络和通讯,当然这个人也可以一边开采此地的宝藏。并且,为了节约时间,队伍在前往开采宝藏的过程中是不会走回头路的。现在你作为队长的助理,已经提供了这幅藏宝图,请你算出探险队所能开采的最大宝藏的价值。 1n1001m1001≤n≤100,1≤m≤100

分析:

不难想到树形dp: 设f[i][j]表示对以 i i 为根的子树中,我们下放了jj个人得到的最大收益。 当在 i i 时不分组的话,显然, f[i][j]=maxf[i][j]=max{ f[Soni][j] f [ S o n i ] [ j ] },将这个作为 f[i][j] f [ i ] [ j ] 的初值 当在 i i 时分组的话,显然在根ii需要留下一个人,则要分 i1 i − 1 个人下去, 那么我们对每个 Soni S o n i 枚举一个下放人数 k k f[i][j]=maxf[i][j]=max{ f[Soni][k]+f[i][jk1]+a[i] f [ S o n i ] [ k ] + f [ i ] [ j − k − 1 ] + a [ i ] } 因为 f[i][j] f [ i ] [ j ] 会被更新,这样 f[i][jk1] f [ i ] [ j − k − 1 ] 就会变,而且我们并不知道 f[i][jk1] f [ i ] [ j − k − 1 ] 是否已经计算过 a[i] a [ i ] 那么我们可以 设 g[i][j] g [ i ] [ j ] 表示以i位根的子树中,我们下放了 j j 个人并且第ii处并没有留人得到的最大收益来解决这个问题, 即将 f[i][j]=max f [ i ] [ j ] = m a x { f[Soni][k]+f[i][jk1]+a[i] f [ S o n i ] [ k ] + f [ i ] [ j − k − 1 ] + a [ i ] } 转化成 f[i][j]=max f [ i ] [ j ] = m a x { f[Soni][k]+g[i][jk1]+a[i] f [ S o n i ] [ k ] + g [ i ] [ j − k − 1 ] + a [ i ] } 在维护完 f[i][j] f [ i ] [ j ] 以后将 g[i][j] g [ i ] [ j ] 维护一下即可 时间复杂度: O(N3) O ( N 3 )

代码:

#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define N 105 using namespace std; struct Node { int To, nxt; }e[2*N]; int f[N][N], g[N][N], cp[N], rp[N], Ls[N], a[N], n, m, cnt; void dfs(int x, int father) { f[x][1] = a[x]; for (int k = Ls[x]; k; k = e[k].nxt) { if (e[k].To == father) continue; int y = e[k].To; dfs(y, x); for (int i = 1; i <= m; i++) cp[i] = max(f[y][i], f[x][i]), rp[i] = max(f[y][i], g[x][i]); for (int i = 1; i <= m; i++) for (int j = 0; j <= i - 1; j++) { cp[i] = max(cp[i], g[x][i-j-1] + f[y][j] + a[x]); rp[i] = max(rp[i], g[x][i-j] + f[y][j]); } for (int i = 1; i <= m; i++) f[x][i] = cp[i], g[x][i] = rp[i]; } return; } int main() { scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n - 1; i++) { int u, v; scanf("%d %d", &u, &v); e[++cnt].To = v, e[cnt].nxt = Ls[u], Ls[u] = cnt; e[++cnt].To = u, e[cnt].nxt = Ls[v], Ls[v] = cnt; } dfs(1, -1); printf("%d\n", f[1][m]); return 0; }
转载请注明原文地址: https://www.6miu.com/read-2619082.html

最新回复(0)