zoukankan      html  css  js  c++  java
  • POJ-2486 Apple Tree 树形DP

    题意:一棵n个点的树,每个点有苹果数vi,每条边长度为1。从树根1出发,你不能走多于m步,走到一个点就能获得该点苹果,问能获得最多苹果是多少个?

    解法:这道题想了挺久的还是没想到只能看题解了。很经典的树形DP了。

    很容易想到设dp[x][j]表示x子树分配j步能获得最多苹果数量,但是这样是不够的,因为很容易发现只有哪些回到了x点的方案的代价才是子树大小*2,但是走到最后的时候其实可以不用回到x点。那么我们就增加状态纬度,设dp[i][j][0/1]:给树i恰好分配j步(回/不回来)i点的最大值。然后分3中情况写状态转移方程:

    因为我们是一边遍历儿子一边做背包,所以在遍历当前y子树的时候,dp[x]可以看成是左边子树结果集,dp[y]就是当前子树结果集,状态转移方程其实就是想办法把这两个结果集结合在一起。那么其实就三种情况:左边回来且现在回来,走边回来走到右边之后不回来了,右边回来走到左边之后不回来了

    dp[x][j][0]=max(dp[x][j][0],dp[x][j-k][0]+dp[y][k-2][0]);   //情况①:表示遍历从左边的子树回来x点之后,去y子树,再回来x点。

    dp[x][j][1]=max(dp[x][j][1],dp[x][j-k][0]+dp[y][k-1][1]);  //情况②:表示从左边回来,去y子树,不回来x点了

    dp[x][j][1]=max(dp[x][j][1],dp[x][j-k][1]+dp[y][k-2][0]);  //情况3:表示从y子树回来,去左边,不回来x点了

    写了这三种情况就可以AC了。这里提一个小细节,网上很多代码dp[x][j][0/1]代表的是给x树分配小于等于j步的最大价值,博主写的是恰好分配j步,这点不同在代码会有小小的不同。

    总的来说,感觉这道题还是不容易想到,首先得有经验发现状态不够时增加一个[0/1]纬度表示,然后的写出正确的状态转移方程才能写对,也只能多做题才能有感觉吧。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    using namespace std;
    const int N=1e2+10;
    int n,m,v[N];
    vector<int> G[N];
    
    int dp[N][N<<1][2];  //dp[i][j][0/1]:给树i分配j步(回/不回来)i点的最大值 
    void dfs(int x,int fa) {
        for (int i=0;i<G[x].size();i++) {
            int y=G[x][i];
            if (y==fa) continue;
            dfs(y,x);
            for (int j=m;j>=0;j--) {  //计算dp[x][j] 
                for (int k=1;k<=j;k++) {  //给儿子y分配k步 
                    if (k>=2) dp[x][j][0]=max(dp[x][j][0],dp[x][j-k][0]+dp[y][k-2][0]);
                    if (k>=1) dp[x][j][1]=max(dp[x][j][1],dp[x][j-k][0]+dp[y][k-1][1]);
                    if (k>=2) dp[x][j][1]=max(dp[x][j][1],dp[x][j-k][1]+dp[y][k-2][0]);
                }
            }
        }
    }
    
    int main()
    {
        while (scanf("%d%d",&n,&m)==2) {
            for (int i=1;i<=n;i++) G[i].clear();
            memset(dp,-0x3f,sizeof(dp));
            for (int i=1;i<=n;i++) scanf("%d",&v[i]),dp[i][0][0]=dp[i][0][1]=v[i];
            for (int i=1;i<n;i++) {
                int x,y; scanf("%d%d",&x,&y);
                G[x].push_back(y);
                G[y].push_back(x);
            }
            dfs(1,0);
            int ans=0; 
            for (int i=0;i<=m;i++) ans=max(ans,max(dp[1][i][0],dp[1][i][1]));
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    sqlalchemy的orm的高级用法,分组,排序,聚合等方法
    flask的SQLAlchemy,连接数据库的增删改查操作
    wtforms的form表单的高级用法
    初始wtforms表单,以及简单使用
    命令启动flask以及自定义命令
    MySQL5.5安装教程
    Java设计模式-策略模式实际应用场景
    Java设计模式-策略模式详解
    Oracle数据库之FORALL与BULK COLLECT语句
    代理模式详解(静态代理和动态代理的区别以及联系)
  • 原文地址:https://www.cnblogs.com/clno1/p/11191652.html
Copyright © 2011-2022 走看看