poj 2486 Apple Tree(树形dp+背包+dfs)(*****)

本文详细解析了POJ 2486题目的解题思路,利用递归思想进行状态转移,并介绍了如何通过邻接表实现算法。通过分析不同路径返回起点的情况,给出了具体的动态规划方程。

http://poj.org/problem?id=2486

 

(1)本题属于只是交叉的题目,核心思想在于递归(状态转移方程的写出),假如要求得 p 点的信息,则可以通过其所有的相邻的点 now 的信息来更新。

      而 now 的信息可以由未访问且与它相邻的点的信息来更新。

(2)由于是树状结构,访问时可能会折回某一个点,然后去另一条支路上,所以走 K 步有返回起点和不返回起点两种形式。

(3)最核心的算法本质:

dp[0][g][i+j+2]=max(dp[0][g][i+j+2], dp[0][g][i]+dp[0][now][j]);
dp[1][g][i+j+1]=max(dp[1][g][i+j+1], dp[0][g][i]+dp[1][now][j]);
dp[1][g][i+j+2]=max(dp[1][g][i+j+2], dp[1][g][i]+dp[0][now][j]);

  这里,0表示返回起点,1表示不返回;g 是所要研究的点,now是它相邻的点钟未访问的点。以上三行代码的目的在于通过now来优化g。(建议画出

  草图分析+1、+2的含义)

(4)本题用到了邻接表,以下两个代码思路一致,邻接表表示有一点点的不同,想不通为什么后者WA。。。

具体代码:

View Code
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
int dp[2][101][205];
int n, k;
bool p[101];
vector<int>eg[101];
void DP(int g)
{
    for(int i=0;i<=k;++i)
        dp[0][g][i]=dp[1][g][i]=dp[0][g][0];
    p[g]=1;
    for(int t=0;t<eg[g].size();t++)
    if(!p[eg[g][t]])
    {
        int now=eg[g][t];
        DP(now);
        for(int i=k-1;i>=0;i--)
            for(int j=k-i;j>=0;j--)
            {
                dp[0][g][i+j+2]=max(dp[0][g][i+j+2], dp[0][g][i]+dp[0][now][j]);
                dp[1][g][i+j+1]=max(dp[1][g][i+j+1], dp[0][g][i]+dp[1][now][j]);
                dp[1][g][i+j+2]=max(dp[1][g][i+j+2], dp[1][g][i]+dp[0][now][j]);
            }
    }
}
int main()
{
    while(scanf("%d%d", &n, &k)!=EOF)
    {
        memset(dp, 0, sizeof(dp));
        memset(p, 0, sizeof(p));
        for(int i=1;i<=n;++i)
        {
            eg[i].clear();
            scanf("%d", &dp[0][i][0]);
        }
        for(int i=1;i<n;i++)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            eg[x].push_back(y);
            eg[y].push_back(x);
        }
        DP(1);
        printf("%d\n", dp[1][1][k]);
    }
    return 0;
}

 未通过的代码(自制的邻接表使用有问题):

View Code
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=120;
int n, k;
int dp[2][maxn][maxn];
bool p[maxn];
struct node
{
    int now, next;
}edge[maxn*maxn];
int tot, head[maxn];
void add_edge(int x, int y)
{
    edge[tot].now=y;
    edge[tot].next=head[x];
    head[x]=tot++;
}
void DP(int g)
{
    for(int i=0;i<=k;++i)
        dp[0][g][i]=dp[1][g][i]=dp[0][g][0];
    p[g]=1;
    for(int t=head[g];t!=-1;t=edge[t].next)
    if(!p[edge[t].now])
    {
        int now=edge[t].now;
        DP(now);
        for(int i=k-1;i>=0;--i)
            for(int j=k-i;j>=0;--j)
            {
                dp[0][g][i+j+2]=max(dp[0][g][i+j+2],dp[0][g][i]+dp[0][now][j]);
                dp[1][g][i+j+1]=max(dp[1][g][i+j+1],dp[0][g][i]+dp[1][now][j]);
                dp[1][g][i+j+2]=max(dp[1][g][i+j+2],dp[1][g][i]+dp[0][now][j]);
            }
    }
}
int main()
{
    while(scanf("%d%d", &n, &k)!=EOF)
    {
        memset(dp, 0, sizeof(dp));
        memset(p, 0, sizeof(p));
        for(int i=1;i<=n;i++)
            scanf("%d", &dp[0][i][0]);
        tot=0;
        memset(head, -1, sizeof(head));
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d", &x, &y);
            add_edge(x, y);
            add_edge(y, x);
        }
        DP(1);
        printf("%d\n", dp[1][1][k]);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/tim11/archive/2012/08/25/2656688.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值