动态规划--大盗阿福

题目大概:

阿福只能偷不相邻的两个商店的钱,共有n个商店,问阿福最多能偷多少钱。

思路:

dp[n]表示前n个商店最多偷的钱数。a[n]表示每个商店的钱数。

1。。。当前商店如果被偷,则dp[n]=dp[n-2]+a[n].

2。。。不被偷,则dp[n]=dp[n-1]。

感想:

一个递推题。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int a[100001],dp[100001];

int main()
{int t,n;
cin>>t;
memset(dp,0,sizeof(dp));
for(int i=0;i<t;i++)
{cin>>n;
for(int j=1;j<=n;j++)
{
    cin>>a[j];
}
dp[1]=a[1];
dp[2]=max(a[1],a[2]);

for(int j=3;j<=n;j++)
{
  dp[j]=max(dp[j-1],dp[j-2]+a[j]);

}

cout<<dp[n]<<endl;
}

    return 0;
}


<think>好的,我现在需要帮助用户解决如何使用Python实现动态规划解决“博物馆大盗问题”。首先,我要明确这个问题是什么。博物馆大盗问题,也就是背包问题的一种,通常是0-1背包问题,即每个物品只能选择拿或不拿,不能分割,目标是最大化价值且不超过背包容量。 用户之前的问题是关于硬币找零的动态规划,现在转到背包问题,可能需要先回顾动态规划的基本概念。不过用户已经有一定的背景,所以应该直接切入主题。 首先,我需要确定用户的具体需求。他们可能需要一个Python实现的示例,包括问题描述、动态规划的思路、代码实现以及示例测试。我需要确保回答结构清晰,逐步引导用户理解问题并实现解决方案。 接下来,我应该考虑动态规划解决0-1背包问题的步骤。通常步骤包括定义状态、状态转移方程、初始化以及填表过程。需要将这些步骤用中文清晰地解释出来,并结合代码进行说明。 另外,用户可能有疑问,比如如何处理物品的选择,如何回溯找到具体拿了哪些物品。所以在代码中,除了计算最大价值,还需要记录路径,这样用户可以看到具体拿了哪些物品。 需要检查代码的正确性,比如数组的索引是否正确,是否处理了边界条件,例如当背包容量为0时的情况。此外,代码的可读性也很重要,变量名要有意义,注释要清晰。 还要考虑可能的优化,比如空间复杂度优化,将二维数组压缩为一维数组。不过对于初学者来说,先展示二维数组的实现可能更容易理解,之后再提到优化方法。 然后,我需要准备一个示例测试用例,比如用户提到的物品列表和背包容量,运行代码并展示结果,确保用户可以看到实际效果。 最后,总结关键点,确保用户理解动态规划解决该问题的核心思想,以及代码的结构和实现细节。可能还需要对比其他方法,比如贪心算法的不足,强调动态规划的优势。 在编写过程中,要注意使用中文术语,避免专业术语的直译,保持口语化,但又要准确。同时,按照用户之前的格式,使用分节和代码块,使结构清晰。</think>--- ### **问题描述:博物馆大盗问题(0-1背包问题)** **场景**:大盗潜入博物馆,有 $n$ 件物品,每件物品价值 $v_i$、重量 $w_i$。背包容量为 $C$。每件物品**只能选择拿或不拿**(不可分割)。目标是**在背包容量限制下,最大化总价值**。 **示例输入**: ```python values = [60, 100, 120] # 每件物品的价值 weights = [10, 20, 30] # 每件物品的重量 capacity = 50 # 背包容量 ``` **期望输出**: ``` 最大总价值: 220 选取物品索引: [2, 1] # 选第2件(重量20,价值100)和第3件(重量30,价值120) ``` --- ### **动态规划解决思路** #### **1. 定义状态** - 定义二维数组 `dp[i][j]`:表示**前 $i$ 件物品**放入**容量为 $j$ 的背包**时,能获得的最大价值。 #### **2. 状态转移方程** - 对于第 $i$ 件物品(索引为 $i-1$): - **不拿**:`dp[i][j] = dp[i-1][j]`(价值不变) - **拿**(若 `j ≥ weights[i-1]`):`dp[i][j] = dp[i-1][j - weights[i-1]] + values[i-1]` - **最终决策**:`dp[i][j] = max(不拿, 拿)` #### **3. 初始化** - `dp[0][j] = 0`(没有物品可选时,价值为0) - `dp[i][0] = 0`(背包容量为0时,无法装任何物品) --- ### **Python代码实现** ```python def knapsack(values, weights, capacity): n = len(values) # 初始化动态规划表 (n+1行,capacity+1列) dp = [[0] * (capacity + 1) for _ in range(n + 1)] # 记录物品选择路径 selected = [[False] * (capacity + 1) for _ in range(n + 1)] # 填充dp表和selected表 for i in range(1, n + 1): for j in range(1, capacity + 1): if weights[i-1] <= j: take_value = dp[i-1][j - weights[i-1]] + values[i-1] not_take_value = dp[i-1][j] if take_value > not_take_value: dp[i][j] = take_value selected[i][j] = True # 标记选择该物品 else: dp[i][j] = not_take_value else: dp[i][j] = dp[i-1][j] # 回溯找到具体选择的物品 max_value = dp[n][capacity] selected_items = [] j = capacity for i in range(n, 0, -1): if selected[i][j]: selected_items.append(i-1) # 记录物品索引(从0开始) j -= weights[i-1] return max_value, selected_items[::-1] # 反转列表使顺序按物品索引排列 # 示例测试 values = [60, 100, 120] weights = [10, 20, 30] capacity = 50 max_value, selected_items = knapsack(values, weights, capacity) print(f"最大总价值: {max_value}") print(f"选取物品索引: {selected_items}") ``` --- #### **输出结果** ``` 最大总价值: 220 选取物品索引: [1, 2] # 对应重量20+30=50,价值100+120=220 ``` --- ### **关键点解释** 1. **动态规划表 `dp` 的构建** - 通过遍历每件物品和每个可能的容量,逐步填充表格。 - 时间复杂度:$O(n \cdot C)$,空间复杂度:$O(n \cdot C)$。 2. **路径回溯** - 通过 `selected` 表记录每一步是否选择物品,最终从后向前回溯,确定具体选择的物品。 3. **优化空间复杂度** - 可将二维数组 `dp` 压缩为一维数组(滚动数组),空间复杂度优化为 $O(C)$。 --- ### **优化版本(一维数组)** ```python def knapsack_optimized(values, weights, capacity): n = len(values) dp = [0] * (capacity + 1) for i in range(n): for j in range(capacity, weights[i]-1, -1): # 逆序遍历防止覆盖 dp[j] = max(dp[j], dp[j - weights[i]] + values[i]) return dp[capacity] # 示例输出:最大总价值仍为220 ``` --- ### **总结** - **动态规划核心思想**:通过子问题的最优解逐步推导全局最优解。 - **适用场景**:物品不可分割、选择依赖前序决策的优化问题。 - **对比贪心算法**:贪心可能无法得到最优解(如按价值密度排序不一定最优)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值