题目链接: POJ 2486 Apple Tree
分析:
这题最重要的一个地方是可能会走回头路.
那么,我们对某一结点可分为两种状态, 即访问其子结点后回来及不回来.
当访问到某一结点的子结点时, 有以下三种情况:
PS. dp[u][i][j] 表示到结点i已经走了j步,得到最多的苹果数. u=0 表示不回到该结点, u=1是回到该点.
Ⅰ、从兄弟结点出发访问son结点, 要经过cnt,所以要+2到达son. 并且不再回到cnt.
Ⅱ、直接从父亲访问son结点,所以到达son只需要 +1, 并且不再回到父亲结点
Ⅲ、直接从cnt结点访问son结点, 访问son的整个树枝之后又回到父亲结点. 因些要+2.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=222;
vector<int>Tree[maxn];
int dp[2][maxn][maxn]; /// 1表示去过子结点回到原来的点, 0相反
int val[maxn];
bool vis[maxn];
int n,m;
void DFS(int cnt){
vis[cnt]=true;
int len=Tree[cnt].size();
for(int i=0;i<=m;++i)
dp[0][cnt][i]=dp[1][cnt][i]=val[cnt];
for(int i=0;i<len;++i){
int son=Tree[cnt][i];
if(vis[son]) continue;
DFS(son);
for(int j=m;j>=0;--j)
for(int k=0;k<=j;++k){
dp[0][cnt][j+1]=max(dp[0][cnt][j+1],dp[1][cnt][j-k]+dp[0][son][k]);
dp[0][cnt][j+2]=max(dp[0][cnt][j+2],dp[0][cnt][j-k]+dp[1][son][k]);
dp[1][cnt][j+2]=max(dp[1][cnt][j+2],dp[1][cnt][j-k]+dp[1][son][k]);
}
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;++i){
Tree[i].clear();
scanf("%d",&val[i]);
}
for(int i=1;i<n;++i){
int a,b; scanf("%d%d",&a,&b);
Tree[a].push_back(b);
Tree[b].push_back(a);
}
DFS(1);
printf("%d\n",dp[0][1][m]);
}
return 0;
}