题目链接
开始学习树形DP的第一题,算是无师自通吧。。。
题目求的是给你一棵数,以及能走的步数,问你走多能吃几颗苹果,如果化成一维的,很容易让人想到运用DP来解这道题,前一刻的步骤会影响后一刻的值,那么如今苹果被挂在了树上,我们就该用树形DP来解了。
树形DP及其推导: 这是道树形DP,题目如是讲到,于是就是处理它了,在这棵树上,我们想拿最多的苹果,首先我们知道从第u个节点向外走,如果一步都不剩了,那么他也能获得u节点上的苹果数,那么再往上,就是u->v,现在根在于v了,对于u,假如u的步数足够,那么它完全可以去v走一遭,甚至返回,去另外的节点,故,这里有个需要考虑的东西就是返回问题,所以DP的设定就得看到返回的问题了,我们即要拿这颗叶子结点上的苹果数,也要拿其他叶子的(就是这么个意思了)。
假如,目前从u点出发,我们怎么看?对于它之下的节点,我们假如已经确定了取值,毕竟dfs()先求到底,我们只要考虑u与u链接的子节点的关系就行,假如步数K足够,那么我们就可以往下走,类似背包的做法,我们可以做如下考虑:“u->v; v->u”就是最后会返回至u,这就比到v就停下来会多2步,“u->v”到v就可以,就是u返回、而v不返回,最后就是v走下去的点,而v是u的子节点,所以就无须考虑了,此法会多出一步,因为要去v的那一步,“u->v; v->u”这步与第一步不同,它是在u出不返回,但在v处得返回,这样会多2步,因为要从v返回回来。
于是,列写DP方程:dp[i][j][k],其中,i为根,j为步数,k为0或1分别表示“不回来”和“回来”的两种情况。
u返回且v也返回情况:dp[u][j+2][1]=max(dp[u][j+2][1], dp[u][k][1] + dp[v][j-k][1]);
u返回而v不返回情况:dp[u][j+1][0]=max(dp[u][j+1][0], dp[u][k][1] + dp[v][j-k][0]);
u不返回而v返回情况:dp[u][j+2][0]=max(dp[u][j+2][0], dp[u][k][0] + dp[v][j-k][1])。
完整代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef long long ll;
const int maxN=105;
int N, K, dp[maxN][maxN<<1][2]={0}, apple[maxN]; //back_'1', no_back-'0'
vector<int> vt[maxN];
bool vis[maxN];
void dfs(int u)
{
vis[u]=true;
for(int i=0; i<=K; i++) dp[u][i][0]=dp[u][i][1]=apple[u];
int len=(int)vt[u].size();
for(int i=0; i<len; i++)
{
int v=vt[u][i];
if(vis[v]) continue;
dfs(v);
for(int j=K; j>=0; j--)
{
for(int k=0; k<=j; k++)
{
dp[u][j+2][1]=max(dp[u][j+2][1], dp[u][k][1]+dp[v][j-k][1]);
dp[u][j+1][0]=max(dp[u][j+1][0], dp[u][k][1]+dp[v][j-k][0]);
dp[u][j+2][0]=max(dp[u][j+2][0], dp[u][k][0]+dp[v][j-k][1]);
}
}
}
}
int main()
{
while(scanf("%d%d", &N, &K)!=EOF)
{
for(int i=1; i<=N; i++){ vt[i].clear(); scanf("%d", &apple[i]); }
memset(vis, false, sizeof(vis));
for(int i=1; i<N; i++)
{
int e1, e2;
scanf("%d%d", &e1, &e2);
vt[e1].push_back(e2);
vt[e2].push_back(e1);
}
dfs(1);
printf("%d\n", max(dp[1][K][0], dp[1][K][1]));
}
return 0;
}