一文详解背包问题

acwing背包问题学习笔记,活动连接

一、01背包问题

在这里插入图片描述
从集合的角度分析dp问题如下:

在这里插入图片描述
代码:

import java.io.*;
import java.util.*;
public class Main{
    public static int[] ve = new int[1010];
    public static int[] w = new int[1010];
    public static int[][] dp = new int[1010][1010];
    public static void main(String[] strs) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String[] nv = in.readLine().split(" ");
        int n = Integer.parseInt(nv[0]),v = Integer.parseInt(nv[1]);
        
        for(int i = 1;i<=n;i++){
            String[] temp = in.readLine().split(" ");
            ve[i] = Integer.parseInt(temp[0]);
            w[i] = Integer.parseInt(temp[1]);
        }
        
        //dp[i,j] = max(dp[i,j],dp[i-1,j],dp[i-1,j-v]+w)
        
        for(int i = 1;i<=n;i++){
            for(int j = 0;j<=v;j++){
                dp[i][j] = dp[i][j]>dp[i-1][j]?dp[i][j]:dp[i-1][j];
                if(j>=ve[i]) dp[i][j] = dp[i][j]>dp[i-1][j-ve[i]]+w[i]?dp[i][j]:dp[i-1][j-ve[i]]+w[i];
            }
        }
        
        System.out.print(dp[n][v]);
    }
}

优化成一维,注意到以下两个事实:

  • 更新dp[i]时只用到了dp[i-1]层的数
  • 更新dp[j]时只用到了dp[j-v]
    故可以先尝试直接把第一维删掉
for(int i = 1;i<=n;i++){
            for(int j = ve[i];j<=v;j++){
                //dp[j] = dp[j]>dp[j]?dp[j]:dp[j];
                dp[j] = dp[j]>dp[j-ve[i]]+w[i]?dp[j]:dp[j-ve[i]]+w[i];
            }
        }

我们来检查一下这样的写法

  • dp[i,j]=dp[i-1,j]没问题
  • dp[i-1][j-ve[i]]是否等于dp[j-ve[i]]? 由于我们循环更新的过程是从左往右更新,所以此时的dp[j-ve[i]]已经被更新过了,但是我们需要用的是上一层没有更新过的dp[j-ve[i]]。要解决这个问题,只需要从后往前遍历更新即可。
import java.io.*;
import java.util.*;
public class Main{
    public static int[] ve = new int[1010];
    public static int[] w = new int[1010];
    public static int[] dp = new int[1010];
    public static void main(String[] strs) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String[] nv = in.readLine().split(" ");
        int n = Integer.parseInt(nv[0]),v = Integer.parseInt(nv[1]);
        
        for(int i = 1;i<=n;i++){
            String[] temp = in.readLine().split(" ");
            ve[i] = Integer.parseInt(temp[0]);
            w[i] = Integer.parseInt(temp[1]);
        }
        
        //dp[i,j] = max(dp[i,j],dp[i-1,j],dp[i-1,j-v]+w)
        
        for(int i = 1;i<=n;i++){
            for(int j = v;j>=ve[i];j--){
                dp[j] = dp[j]>dp[j-ve[i]]+w[i]?dp[j]:dp[j-ve[i]]+w[i];
            }
        }
        
        System.out.print(dp[v]);
    }
}

二、完全背包问题

完全背包问题和01背包问题的区别仅仅在于每种物品可以使用无限次。仍然在集合的角度分析。

在这里插入图片描述

在这里插入图片描述
其实可以看出01背包问题只是完全背包问题的一个k<=1的特例,代码:

import java.io.*;
import java.util.*;
import java.math.*;
public class Main{
    public static int[] ve = new int[1010];
    public static int[] w = new int[1010];
    public static int[][] dp = new int[1010][1010];
    public static void main(String[] strs) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String[] nv = in.readLine().split(" ");
        int n = Integer.parseInt(nv[0]),v = Integer.parseInt(nv[1]);
        
        for(int i = 1;i<=n;i++){
            String[] temp = in.readLine().split(" ");
            ve[i] = Integer.parseInt(temp[0]);
            w[i] = Integer.parseInt(temp[1]);
        }
        
        //dp[i,j] = max(dp[i,j],dp[i-1,j],dp[i-1,j-v]+w)
        
        for(int i = 1;i<=n;i++){
            for(int j = 0;j<=v;j++){
                for(int k = 0;k*ve[i]<=j;k++){
                    dp[i][j] = Math.max(dp[i][j],dp[i-1][j-k*ve[i]]+w[i]);
                }
            }
        }
        
        System.out.print(dp[n][v]);
    }
}

如何优化:
在这里插入图片描述

当我们把状态转移方程写出来时,我们可以得到递推式:

  • f[i,j] = max(f[i-1,j],f[i,j-v]+w)

这样我们就可以继续利用01背包问题中的降维方法优化了。
首先优化状态转移方程:

for(int i = 1;i<=n;i++){
    for(int j = 0;j<=v;j++){
        dp[i][j] = dp[i-1][j];
        if(j>=ve[i]) dp[i][j] = Math.max(dp[i][j],dp[i][j-ve[i]]+w[i]);
    }
}

然后消去其中的第一维:

for(int i = 1;i<=n;i++){
    for(int j = ve[i];j<=v;j++){
        dp[j] = Math.max(dp[j],dp[j-ve[i]]+w[i]);
    }
}

由于这里用到的是dp[i][j-ve[i]],所以不存在01背包问题中需要从后往前遍历的问题。

我们再来关注一下优化后状态转移方程:

  • f[i,j] = max(f[i-1,j],f[i,j-v]+w)

他也能用集合划分的观点来解释:
在这里插入图片描述

  • 不加第i个物品时,显然f[i,j] = f[i-1,j]
  • 如果加了第i个物品,那么至少加一个,即f[i,j-v]+w

三、多重背包问题

在这里插入图片描述
弄懂完全背包问题后,多重背包问题就很简单了,只需要在枚举第i个物品加入的个数时判断是否大于Si即可。

import java.io.*;
import java.util.*;
import java.math.*;
public class Main{
    public static int[] ve = new int[110],w = new int[110],s = new int[110];
    public static int[][] dp = new int[110][110];
    public static void main(String[] args) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String[] nv = in.readLine().split(" ");
        int n = Integer.parseInt(nv[0]),v = Integer.parseInt(nv[1]);
        
        for(int i = 1;i<=n;i++){
            String[] temp = in.readLine().split(" ");
            ve[i] = Integer.parseInt(temp[0]);
            w[i] = Integer.parseInt(temp[1]);
            s[i] = Integer.parseInt(temp[2]);
        }
        
        for(int i = 1;i<=n;i++){
            for(int j = 0;j<=v;j++){
                for(int k = 0;k<=s[i]&&j-k*ve[i]>=0;k++){
                    dp[i][j] = Math.max(dp[i][j],dp[i-1][j-k*ve[i]]+w[i]*k);
                }
            }
        }
        
        System.out.print(dp[n][v]);
    }
}

四、分组背包问题

在这里插入图片描述
本质上还是一个完全背包问题,由于每组物品只能选一个加入背包,所以前i个物品和前i组物品没什么区别,在枚举第i个物品时,将第i组物品中的所有物品全部枚举一遍即可。

import java.io.*;
import java.util.*;
import java.math.*;
public class Main{
    public static Map<Integer,int[][]> map = new HashMap<>(); 
    public static int[][] dp = new int[110][110];
    public static void main(String[] args) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String[] nv = in.readLine().split(" ");
        int n = Integer.parseInt(nv[0]),v = Integer.parseInt(nv[1]);
        
        for(int i = 1;i<=n;i++){
            int s = Integer.parseInt(in.readLine());
            int[][] temp = new int[s][2];
            for(int j = 0;j<s;j++){
                String[] vw = in.readLine().split(" ");
                temp[j][0] = Integer.parseInt(vw[0]);
                temp[j][1] = Integer.parseInt(vw[1]);
            }
            map.put(i,temp);
        }
        
        for(int i = 1;i<=n;i++){
            for(int j = 0;j<=v;j++){
                int[][] temp = map.get(i);//第i组背包
                dp[i][j] = dp[i-1][j];
                for(int k = 0;k<temp.length;k++){
                    if(j-temp[k][0]>=0) dp[i][j] = Math.max(dp[i][j],dp[i-1][j-temp[k][0]]+temp[k][1]);
                }
            }
        }
        
        System.out.print(dp[n][v]);
        
    }
}
### 数据治理框架的概念 数据治理框架是一套结构化的方法论,用于指导企业在管理和利用数据资产的过程中如何有效地进行决策和支持操作。该框架不仅涵盖了技术和工具的选择,还包括政策制定、标准设定以及流程设计等方面的内容[^3]。 ### 数据治理框架的主要构成要素 #### 1. 利益干系人的定义与角色分配 在DGI数据治理框架中,明确了三个主要的利益干系人群体:数据利益相关者、数据治理办公室和数据专员。这些群体共同构成了数据治理的核心团队,在整个过程中扮演着不同的角色并承担相应的责任[^1]。 - **数据利益相关者**:通常指那些对企业内部或外部使用的各类数据有直接影响的人士,他们可能来自业务部门或是信息技术领域。 - **数据治理办公室**:作为协调中心负责监督整体策略执行情况,并确保各个层面遵循既定方针;同时还要处理日常运营事务如培训教育等。 - **数据专员**:具体落实各项措施的技术人员,专注于特定项目或任务上的细节工作。 #### 2. 职责分工说明 对于上述提到的不同参与者之间的协作方式及各自的任务边界进行了详细的阐述。这有助于避免重复劳动的同时也能够提高工作效率,使得每一个人都清楚自己在整个体系内的位置及其所肩负的责任所在。 #### 3. 知识领域的划分 依据DAMA-DMBOK框架,数据管理被细分为多个相互关联却又独立存在的知识域。这种分类方法可以帮助组织更好地理解复杂的信息生态系统,并针对不同方面采取针对性更强的解决方案。例如,“元数据管理”、“文档记录”、“安全保护”等领域都属于这一范畴内的重要组成部分[^2]。 ```python # Python代码示例展示了一个简单的类来表示数据治理框架中的角色 class DataGovernanceRole: def __init__(self, name, responsibilities): self.name = name self.responsibilities = responsibilities def describe_role(self): print(f"The role of {self.name} includes:") for responsibility in self.responsibilities: print(f"- {responsibility}") data_stakeholder = DataGovernanceRole( "Data Stakeholders", ["Influencing data usage within or outside the organization"] ) governance_office = DataGovernanceRole( "Data Governance Office", [ "Overseeing strategy implementation", "Ensuring compliance with policies" ] ) data_specialist = DataGovernanceRole( "Data Specialists", ["Implementing specific measures on projects"] ) data_stakeholder.describe_role() governance_office.describe_role() data_specialist.describe_role() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值