• noip2006 金明的预算


    题目链接:传送门

    题目大意:略。。

    题目思路:其实单就这道题来说,一个主件最多两个附件,且附件不再包含附件,所以很简单,但是如果主件的附件无限制,附件也可包含无限制的附件,应该怎么做?

         首先推荐一篇论文  徐持衡的《浅谈几类背包题》,里面有详细的讲解,这一类树形依赖背包问题最终都能优化到  n*c 复杂度,n为物品数,c为背包容量

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <algorithm>
    #include <cstring>
    #include <stack>
    #include <cctype>
    #include <queue>
    #include <string>
    #include <vector>
    #include<functional>
    #include <set>
    #include <map>
    #include <climits>
    #define lson root<<1,l,mid
    #define rson root<<1|1,mid+1,r
    #define fi first
    #define se second
    #define ping(x,y) ((x-y)*(x-y))
    #define mst(x,y) memset(x,y,sizeof(x))
    #define mcp(x,y) memcpy(x,y,sizeof(y))
    using namespace std;
    #define gamma 0.5772156649015328606065120
    #define MOD 1000000007
    #define inf 0x3f3f3f3f
    #define N 1000005
    #define maxn 32005
    typedef pair<int,int> PII;
    typedef long long LL;
    
    int n,m,x;
    vector<int>V[1005];
    int dp[105][maxn];
    int w[1005],v[1005];
    void dfs(int u,int m){
        if(m<=0)return;
        for(int e:V[u]){
            for(int k=0;k<=m;++k)dp[e][k]=dp[u][k];
            dfs(e,m-w[e]);
            for(int k=w[e];k<=m;++k)dp[u][k]=max(dp[u][k],dp[e][k-w[e]]+v[e]);
        }
    }
    int main(){
        int i,j,group,x,y;
        while(scanf("%d%d",&m,&n)!=EOF){ 
            mst(dp,0);
            for(i=0;i<=n;++i)V[i].clear();
            for(i=1;i<=n;++i){
                scanf("%d%d%d",&w[i],&v[i],&x);
                v[i]*=w[i];
                V[x].push_back(i);
            }
            dfs(0,m);
            printf("%d
    ",dp[0][m]);
        }
        return 0;
    }

    然后再从这道题谈谈我对树形依赖背包的理解,不管怎么想怎么做,这是背包问题。

    也就是对于一个物品,存在拿与不拿的情况,而如果不拿父节点,那么子节点也就只有不拿的情况。

    加上树形结构以后,我们依然要保持背包的性质不变,也就是维护容量V包含价值最优的情况。

    因为N*C^2复杂度有太多优秀的博客介绍,这里就不再赘述。

    讲下个人对于 N*C  复杂度解法的理解。首先 二维数组 dp[u][V] ,u代表当前处理的节点u, V代表当前处理的容量(V<=背包总容量),整个 dp[u][V]保存的是处理到节点u容量V时的最优价值(处理到u,代表u节点左边所有的节点都已处理完毕)以及u所有子节点容量为V的最优值。

    我们在处理节点时,存在拿与不拿的情况,而想选子节点则必须拿。那么假设处理到了节点x,我们可以先强制拿节点 x,这样就可以很方便的处理x的子节点,递归下去,对于子节点也同样处理,这样递归到底层时,我们把能拿的子节点都拿完了,但是这不一定是最优解,所以回溯的时候,则需要对拿与不拿进行取舍,因为是从子节点回溯,并且我们递归的时候是强制拿父节点的,因此可以直接选择 拿不拿子节点,而不用管父节点的限制,这样回溯维护最优值。

     

    vector<int>V[1005];
    int dp[105][maxn];
    int w[1005],v[1005];
    void dfs(int u,int m){    ///u 父节点,m 当前可用容量(m<总容量)
        if(m<=0)return;   ///没有可用容量,背包装满了。。
        for(int e:V[u]){  /// e 子节点
            for(int k=0;k<=m-w[e];++k)dp[e][k]=dp[u][k];  ///    * 1 *
            dfs(e,m-w[e]);   ///强制拿e,方便处理e子节点     * 2 *
            for(int k=w[e];k<=m;++k)dp[u][k]=max(dp[u][k],dp[e][k-w[e]]+v[e]);
                             ///回溯更新 拿e后的最优值 与 不拿e ,保存两者最优值
        }
    }

    在分析 * 1 *与 * 2 *;

    * 1 *:和学长讨论了下是否有必要加上这个赋值循环?当然需要。对于节点e来说,强制拿e是在处理完e之前所有节点的基础上进行的,也就是在之前节点最优值的基础上进行的。

         否则对于 e 来说,它与整个树的关系脱离,它最后得到的值与与父节点保存的值没有比较的意义。

    * 2 *:m-w[e]就代表强制拿了e,因为背包中已经有部分体积被e 所占,e的价值只需回溯的时候加上进行更新即可。但是这里似乎没有体现出 强制拿 u?因为依赖背包题目一般给

       的是一片森林。我们为了方便处理,可以加上一个虚拟树根,重量价值都为0,这样调用的时候,直接dfs(虚拟树根,背包总容量)。

    不过感觉* 1 *处还是没有解释好,希望能有大牛补充,有不对的地方也请指出,,,

  • 相关阅读:
    【转】Android中设置TextView的颜色setTextColor
    【转】android颜色对应的xml配置值
    【转】color颜色十六进制编码大全
    【转】Usage of sendBroadcast()
    【转】Android4.3 蓝牙BLE初步
    【转】android中重复连接ble设备导致的连接后直接返回STATE_DISCONNECTED的解决办法---不错不错,重新连接需要花费很长的时间
    【转】推荐--《Android深入浅出》----不错
    【转】google chrome如何设置主页
    【转】Chrome保存mhtml网页文件的方法 – 无需任何插件,完美!
    【转】Android--广播BroadcastReceiver
  • 原文地址:https://www.cnblogs.com/Kurokey/p/5717164.html
Copyright © 2020-2023  润新知