zoukankan      html  css  js  c++  java
  • P2014 选课 (依赖性背包,树上背包) U53204 【数据加强版】选课(后序遍历优化)

    这道背包和普通的背包不一样,这题的物品是有依赖,即要选这个物品必须要先选它的前置物品才行,这就变成了依赖性背包,很明显这样会就产生森林,森林的的话不好处理,加个价值为0的虚拟节点0就可以组成一棵树,那就好处理多了.

    dp[i][j][k]表示在i的子树里从前j棵子树里取若干个物品放入容量k的包里能得到的最大价值,一般的树形dp里,父节点的dp值都是儿子转移来的。

    可以把每个子树视为一个泛化物品,一开始搜到u时,u子树的泛化物品只包括u物品,然后通过把vi子树的泛化物品求出来,然后依次和u节点的泛化物品合并(这个合并就是用的2个泛化物品合并的方法,先枚举k,包容量,然后枚举v,分配给包u的容量)就可以得到u子树的泛化物品。然后因为要选物品必须要先选它的前置物品才行,所以我们

    对每个初始化 for(int k=1;k<=m+1;k++) dp[u][0][k]=val[u],这样子才能保证最后得到的u子树的泛化物品的dp[u][j](j>=1)选取了u物品。

    原版:n*m^2          这篇论文有nm的泛化物品优化方法,但是我看不懂

    #include<bits/stdc++.h>
    using namespace std;
    #define ls rt<<1
    #define rs (rt<<1)+1
    #define ll long long
    #define fuck(x) cout<<#x<<"     "<<x<<endl;
    const int maxn=1e6+10;
    int d[4][2]={1,0,-1,0,0,1,0,-1};
    int dp[305][305][305],val[305],n,m;
    bool vis[305];
    vector<int>g[305];
    
    void dfs(int u){
        for(int k=1;k<=m+1;k++) dp[u][0][k]=val[u];
        for(int i=0;i<g[u].size();i++){
            dfs(g[u][i]);
            for(int k=1;k<=m+1;k++){
                dp[u][i+1][k]=dp[u][i][k];
                for(int v=1;v<=k;v++)
                    dp[u][i+1][k]=max(dp[u][i+1][k],dp[u][i][v]+dp[g[u][i]][(int)(g[g[u][i]].size())][k-v]);
            }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            int tmp;
            scanf("%d%d",&tmp,&(val[i]));
            if(tmp!=0)
                vis[i]=1,g[tmp].push_back(i);
        }
        for(int i=1;i<=n;i++)
            if(!vis[i])
                g[0].push_back(i);
        dfs(0);
        cout<<dp[0][(int)g[0].size()][m+1]<<endl;
        return 0;
    }
    v#include<bits/stdc++.h>
    using namespace std;
    #define ls rt<<1
    #define rs (rt<<1)+1
    #define ll long long
    #define fuck(x) cout<<#x<<"     "<<x<<endl;
    const int maxn=1e6+10;
    int d[4][2]={1,0,-1,0,0,1,0,-1};
    int dp[305][305],val[305],n,m;
    bool vis[305];
    vector<int>g[305];
    
    void dfs(int u){
        for(int k=1;k<=m+1;k++) dp[u][k]=val[u];
        for(int i=0;i<g[u].size();i++){
            dfs(g[u][i]);
            for(int k=m+1;k>=1;k--){
                for(int v=1;v<=k;v++)
                    dp[u][k]=max(dp[u][k],dp[u][v]+dp[g[u][i]][k-v]);
            }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            int tmp;
            scanf("%d%d",&tmp,&(val[i]));
            if(tmp!=0)
                vis[i]=1,g[tmp].push_back(i);
        }
        for(int i=1;i<=n;i++)
            if(!vis[i])
                g[0].push_back(i);
        dfs(0);
        cout<<dp[0][m+1]<<endl;
        return 0;
    }

    加强版:

    这题数据范围很大,用之前的方法过不了,用了后序遍历来优化,求出树的后序遍历之和,求这个遍历序列对应的物品做一次类似01背包的dp就好了,dp[i][j]表示从前i个物品选若干物品放容量j的包的最大价值,转移方程是dp[i][j]=max(dp[i-1][j-1]+wi,dp[i-sz[i]][j])

    #include<bits/stdc++.h>
    using namespace std;
    #define ls rt<<1
    #define rs (rt<<1)+1
    #define ll long long
    #define fuck(x) cout<<#x<<"     "<<x<<endl;
    const int maxn=1e5+10;
    int d[4][2]= {1,0,-1,0,0,1,0,-1};
    inline ll read() {
        ll s = 0,w = 1;
        char ch = getchar();
        while(!isdigit(ch)) {
            if(ch == '-') w = -1;
            ch = getchar();
        }
        while(isdigit(ch))
            s = s * 10 + ch - '0',ch = getchar();
        return s * w;
    }
    inline void write(ll x) {
        if(x < 0)
            putchar('-'), x = -x;
        if(x > 9)
            write(x / 10);
        putchar(x % 10 + '0');
    }
    int n,m,val[maxn],afo[maxn],sz[maxn],cnt;
    vector<int>g[maxn],dp[maxn];
    
    void dfs(int now){
        sz[now]=1;
        for(int i=0;i<g[now].size();i++)
            dfs(g[now][i]),sz[now]+=sz[g[now][i]];
        afo[++cnt]=now;
    }
    
    int main()
    {
        n=read(),m=read();
        for(int i=0; i<=n+1; i++) {
            dp[i].reserve(m+3);
            for (int j = 1; j <= m + 2; j++)
                dp[i].push_back(0);
        }
        for(int i=1; i<=n; i++)
        {
            int tmp;
            tmp=read(),val[i]=read();
            g[tmp].push_back(i);
        }
        dfs(0);
        for(int i=1;i<=cnt;i++){
            for(int j=1;j<=m+1;j++){
                dp[i][j]=max(dp[i-sz[afo[i]]][j],dp[i-1][j-1]+val[afo[i]]);
            }
        }
        write(dp[cnt][m+1]);
        puts("");
        return 0;
    }
  • 相关阅读:
    C# 应用
    C# 基础
    C# 基础
    C# 基础
    vs
    C# 基础
    C# 基础
    C# 基础
    C# 基础
    C# 基础
  • 原文地址:https://www.cnblogs.com/eason9906/p/11754704.html
Copyright © 2011-2022 走看看