题目大意
给定一棵树,树有权值。
有两个操作:
1.走一条边
2.获得一个点的权值(权值获得后清零)
两个操作均花费1单位时间
固定从1号点出发,在m个单位时间内最多能获得多少贡献?
数据范围
对于10%的数据,N≤20。
对于50%的数据,N≤110。
对于100%的数据1 ≤ N, M ≤ 500,1 ≤ A[i]≤ 10^6。
第5到第10个测试点都有多个子测试。
Solution
10%:爆搜
时间复杂度:O(
)
100%:直接DP,设f[i][j][0..1]
f[i][j][0]表示以 i 为根的子树中,花费 j 单位时间,最
终回到 i 的最大收益。
f[i][j][1]表示以 i 为根的子树中,花费 j 单位时间,最
终不必回到 i 的最大收益。
转移显然,注意细节处理
时间复杂度:O(
)
Code
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5e2 + 5;
struct Edge
{
int to,next;
}edge[N << 1];
int n,m,tot,x,y,ans;
int a[N],st[N],f[N][N][2],vis[N];
void link(int u,int v)
{
edge[++tot].to = v;
edge[tot].next = st[u];
st[u] = tot;
}
void dfs(int x)
{
vis[x] = 1;
for (int l = st[x] ; l ; l = edge[l].next)
{
int v = edge[l].to;
if (!vis[v])
{
dfs(v);
for (int j = m ; j >= 0 ; j--)
for (int k = m - j ; k >= 0 ; k--)
{
if (j + k + 1 <= m) f[x][j + k + 1][1] = max(f[x][j + k + 1][1],f[x][j][0] + f[v][k][1]);
if (j + k + 2 <= m) f[x][j + k + 2][0] = max(f[x][j + k + 2][0],f[x][j][0] + f[v][k][0]);
if (j + k + 2 <= m) f[x][j + k + 2][1] = max(f[x][j + k + 2][1],f[x][j][1] + f[v][k][0]);
}
}
}
for (int j = m ; j >= 1 ; j--)
f[x][j][0] = max(f[x][j][0],f[x][j - 1][0] + a[x]),f[x][j][1] = max(f[x][j][1],f[x][j - 1][1] + a[x]);
}
int main()
{
freopen("dostavljac.in","r",stdin);
freopen("dostavljac.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i = 1 ; i <= n ; i++) scanf("%d",&a[i]);
for (int i = 1 ; i < n ; i++) scanf("%d%d",&x,&y),link(x,y),link(y,x);
dfs(1);
printf("%d\n",max(f[1][m][0],f[1][m][1]));
fclose(stdin);
fclose(stdout);
return 0;
}