题目:http://poj.org/problem?id=2486
【题意】:给一棵树,每个节点上有一些苹果,根节点编号为1,从根节点开始走,一共走K步(一步走一条边),问最多能吃到多少苹果
【思路】:dp~~ 这个题的状态设置有多种方法,此题有一个特性,即:对于每一种走法,所走子树的顺序不影响最终的结果
①dp[i][j][0/1],以i为根节点,最多可以走j步最后回到(即1)i点,最多吃多少苹果,不一定回到(即0),最多吃多少苹果
dp[root][j][1]=max(dp[root][j][1],dp[go][t][1]+dp[root][j-2-t][1]); go表示root的儿子
dp[root][j][0]=max(dp[root][j][0],dp[go][t][1]+dp[root][j-t-2][0]); 先从这棵子树上回来,再走其他的子树
dp[root][j][0]=max(dp[root][j][0],dp[go][t][0]+dp[root][j-t-1][1]); 先从其他的子树上回来,再去这棵子树上,不回到i
这也是我一开始的想法,不过WA了10次。。
原因有二:算dp[root][j][0]之前不能将将dp[root][j][1]算出来=_=(请观察转移方程,巨坑),还有就是初始化(后文再谈)
②dp[i][j][0/1],以i为根节点,最多可以走j步最后回到(即1)i点,最多吃多少苹果;一定不回到(即0),最多吃多少苹果
以某个节点为根走K步,结果最大的一定是dp[i][K][0],这是因为最后一步回到i,还不如在子树上闲逛。。
dp[root][j][1]=max(dp[root][j][1],dp[go][t][1]+dp[root][j-2-t][1]);
dp[root][j][0]=max(dp[root][j][0],dp[go][t][1]+dp[root][j-t-2][0](上文已说明为何不是1));先去这棵子树,返回,再去其他子树
dp[root][j][0]=max(dp[root][j][0],dp[go][t][0](上文已说明为何不是1)+dp[root][j-t-1][1]);先去其他子树返回,再去这棵子树
两种方式的转移方程是一样的。。。但②中运用了贪心,,不然方程要复杂一些
现在假设 root 为根的所有儿子的状态都已求出,即dp[所有的go][0~K][0/1]都求出来了,怎样算出root的所有状态呢?
考虑每一棵子树,每棵子树假定只能走一次(走过了,再走是没有用的),只有走或不走的情况, 对应选或不选的情况
01背包了,不过这里比较特殊,在选的状态里 不返回 ,会使后面的子树都不能选择(这也是状态设置为两个的原因),但不必理会,转移方程已体现了这一点。
仔细体会01背包
最后谈一下初始化(简直无语巨坑),按照①的想法来,对于每一个节点dp[i][0-K][0/1]=num[i],最多走K步,若把i的子树遍历完了,k还有多的,反正是最多,后面不走便是。
【代码】
按照①的想法来写
<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct Edge
{
int go,next;
}edge[2*110];
int head[110],tot;
void addedge(int a,int b)
{
edge[tot].go=b;
edge[tot].next=head[a];
head[a]=tot++;
}
int N,K;
int dp[110][210][2];
int num[110];
void dfs(int root,int fa)
{
for(int i=head[root];i!=-1;i=edge[i].next)
{
int go=edge[i].go;
if(go==fa)continue;
dfs(go,root);
for(int j=K;j>=0;j--)
{
//cout<<"root = "<<root<<ends<<dp[root][j][0]<<endl;
for(int t=0;t<=j-1;t++)
{
if(t<j-1)dp[root][j][1]=max(dp[root][j][1],dp[go][t][1]+dp[root][j-2-t][1]);
if(t<j-1)dp[root][j][0]=max(dp[root][j][0],dp[go][t][1]+dp[root][j-t-2][0]);
dp[root][j][0]=max(dp[root][j][0],dp[go][t][0]+dp[root][j-t-1][1]);
}
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(scanf("%d%d",&N,&K)!=EOF)
{
memset(head,-1,sizeof(head));
tot=0;
for(int i=1;i<=N;i++)
{
scanf("%d",&num[i]);
for(int j=0;j<=K;j++)
dp[i][j][0]=dp[i][j][1]=num[i];
}
for(int i=1;i<N;i++)
{
int a,b;
scanf("%d%d",&a,&b);
addedge(a,b);
addedge(b,a);
}
dfs(1,-1);
// for(int i=1;i<=N;i++)
// {
// cout<<"i = "<<i<<endl;
// for(int j=0;j<=K;j++)
// cout<<dp[i][j][0]<<ends;
// cout<<endl;
// }
int res=dp[1][K][0];
cout<<res<<endl;
}
return 0;
}
</span>