diff --git a/Algorithm/DP-DynamicProgramming/P1216 [IOI 1994 USACO1.5] 数字三角形 Number Triangles.cpp b/Algorithm/DP-DynamicProgramming/P1216 [IOI 1994 USACO1.5] 数字三角形 Number Triangles.cpp new file mode 100644 index 0000000..9e2a9f0 --- /dev/null +++ b/Algorithm/DP-DynamicProgramming/P1216 [IOI 1994 USACO1.5] 数字三角形 Number Triangles.cpp @@ -0,0 +1,33 @@ +#include +#include +using namespace std; +int main(){ + int n; + cin >> n; + vector> arr(n); + vector> dp(n); + int tmp; + for(int i = 0; i < n; i++){ + for(int j = 0; j < i + 1; j++){ + cin >> tmp; + arr[i].push_back(tmp); + dp[i].push_back(-1); + } + } + dp[0][0] = arr[0][0]; + for(int i = 1; i < n; i++){ + for(int j = 0; j < i + 1; j++){ + if(j == 0) dp[i][j] = dp[i - 1][j] + arr[i][j]; + else if(j == i) dp[i][j] = dp[i - 1][j - 1] + arr[i][j]; + else{ + dp[i][j] = max(dp[i - 1][j - 1] + arr[i][j], dp[i - 1][j] + arr[i][j]); + } + } + } + int max = -2; + for(int i = 0; i < n; i++){ + if(dp[n - 1][i] > max) max = dp[n - 1][i]; + } + cout << max << endl; + return 0; +} diff --git a/Algorithm/DP-DynamicProgramming/P1616 疯狂的采药.cpp b/Algorithm/DP-DynamicProgramming/P1616 疯狂的采药.cpp new file mode 100644 index 0000000..5b6736c --- /dev/null +++ b/Algorithm/DP-DynamicProgramming/P1616 疯狂的采药.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +using namespace std; +typedef long long ll; +int main(){ + ll t, m; + cin >> t >> m; + vector> arr(m); + vector dp(t + 1, 0); + for (int i = 0; i < m; i++) { + cin >> arr[i].first >> arr[i].second; + } + //״̬-> dp[i] = max(dp[i - time[i]] + value[i]) + for(int i = 1; i < t + 1; i++){ + for(int j = 0; j < m; j++){ + if(i - arr[j].first >= 0) dp[i] = max(dp[i], dp[i - arr[j].first] + arr[j].second); + } + } + cout << dp[t] << endl; + return 0; +} diff --git a/Algorithm/DP-DynamicProgramming/P1802 5 倍经验日.cpp b/Algorithm/DP-DynamicProgramming/P1802 5 倍经验日.cpp new file mode 100644 index 0000000..25b59f0 --- /dev/null +++ b/Algorithm/DP-DynamicProgramming/P1802 5 倍经验日.cpp @@ -0,0 +1,7 @@ +#include +#include +using namespace std; +int main(){ + + return 0; +} diff --git a/Algorithm/DP-DynamicProgramming/P2842 纸币问题 1.cpp b/Algorithm/DP-DynamicProgramming/P2842 纸币问题 1.cpp new file mode 100644 index 0000000..8ddb37c --- /dev/null +++ b/Algorithm/DP-DynamicProgramming/P2842 纸币问题 1.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +using namespace std; + +int main() { + int n, w; + cin >> n >> w; + vector a(n); + for (int i = 0; i < n; i++) cin >> a[i]; + + vector dp(w + 1, 1e9); + dp[0] = 0; + + for (int j = 0; j < n; j++) { + for (int i = a[j]; i <= w; i++) { + dp[i] = min(dp[i], dp[i - a[j]] + 1); + } + } + + if (dp[w] == 1e9) cout << -1 << endl; // ޽ + else cout << dp[w] << endl; + + return 0; +} + diff --git a/Algorithm/DP-DynamicProgramming/readme.md b/Algorithm/DP-DynamicProgramming/readme.md index 795c64b..23ffabd 100644 --- a/Algorithm/DP-DynamicProgramming/readme.md +++ b/Algorithm/DP-DynamicProgramming/readme.md @@ -1,3 +1,157 @@ +好的,我们来系统、仔细地讲解 **动态规划(Dynamic Programming, DP)**,从基本概念到方法,再到具体例子,让你完全弄明白它的本质和技巧。 + +--- + +## **1️⃣ 动态规划的核心思想** + +动态规划的本质是 **将复杂问题分解成子问题,并利用子问题的最优解构建原问题的最优解**。 + +**关键点**: + +1. **最优子结构(Optimal Substructure)** + + * 一个问题的最优解可以由子问题的最优解组合得到。 + * 例子:斐波那契数列 `F(n) = F(n-1) + F(n-2)`,求 `F(n)` 最优解依赖于 `F(n-1)` 和 `F(n-2)`。 + +2. **重叠子问题(Overlapping Subproblems)** + + * 子问题会被重复计算,如果每次都重新计算,效率低。 + * 例子:递归计算斐波那契,`F(3)` 会在 `F(5)` 和 `F(4)` 中重复计算。 + +3. **记忆化或表格存储** + + * 把子问题的解存起来,下次遇到就直接用,避免重复计算。 + +--- + +## **2️⃣ DP 的基本步骤** + +解决一个 DP 问题通常有如下步骤: + +### **步骤 1:定义状态(State)** + +* 状态描述了问题在某个阶段的“局面”或“子问题”。 +* 例子: + + * 斐波那契:`dp[i]` 表示第 `i` 个数的值 + * 背包问题:`dp[i][w]` 表示前 `i` 件物品放入容量为 `w` 的背包能达到的最大价值 + +### **步骤 2:状态转移方程(State Transition)** + +* 用子状态的解推导当前状态的解 +* 例子: + + * 斐波那契:`dp[i] = dp[i-1] + dp[i-2]` + * 背包问题:`dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight[i]] + value[i])` + +### **步骤 3:初始条件** + +* 明确最小子问题的解 +* 例子: + + * 斐波那契:`dp[0] = 0, dp[1] = 1` + * 背包问题:`dp[0][w] = 0` + +### **步骤 4:计算顺序** + +* **自底向上**(迭代填表):先算小子问题,再组合大问题 +* **自顶向下**(递归 + 记忆化):用递归解决子问题,遇到重复就查表 + +--- + +## **3️⃣ DP 示例** + +### **示例 1:斐波那契数列** + +```cpp +int fib(int n) { + int dp[1000]; + dp[0] = 0; + dp[1] = 1; + for(int i=2; i<=n; i++) + dp[i] = dp[i-1] + dp[i-2]; + return dp[n]; +} +``` + +* 状态:`dp[i]` +* 转移:`dp[i] = dp[i-1] + dp[i-2]` +* 时间复杂度:O(n) +* 空间复杂度:O(n),优化后可 O(1) + +--- + +### **示例 2:背包问题(0-1 背包)** + +```cpp +int dp[1000][1000]; // dp[i][w] = 前i件物品放入容量为w的背包最大价值 +for(int i=0;i<=n;i++) + for(int w=0;w<=W;w++){ + if(i==0 || w==0) dp[i][w] = 0; + else if(weight[i] <= w) + dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight[i]] + value[i]); + else + dp[i][w] = dp[i-1][w]; + } +``` + +* 状态:`dp[i][w]` 表示前 `i` 件物品在容量为 `w` 时最大价值 +* 转移:选择放或不放第 `i` 件物品 +* 复杂度:O(n\*W) + +--- + +## **4️⃣ 动态规划的类型** + +1. **线性 DP** + + * 状态是一维,依赖前几个状态 + * 例:斐波那契、最长递增子序列 + +2. **区间 DP** + + * 状态是区间 `[i,j]` + * 例:矩阵连乘、石子合并、最优博弈 + +3. **背包 DP** + + * 状态包括容量和物品选择 + * 例:0-1 背包、完全背包、多重背包 + +4. **树形 DP** + + * 状态是树节点,依赖子节点 + * 例:树上最大独立集、树上 DP + +5. **状态压缩 DP** + + * 用位掩码表示状态,适合小规模组合问题 + * 例:TSP(旅行商问题) + +--- + +## **5️⃣ 动态规划 vs 递归 vs 贪心** + +* **DP vs 递归**:DP 是递归的优化版(记忆化/迭代) +* **DP vs 贪心**:贪心只选局部最优,不保证全局最优;DP 会考虑所有子问题组合 + +--- + +### ✅ 核心总结 + +1. DP 核心是 **子问题最优 + 记忆化存储** +2. **步骤**:定义状态 → 状态转移 → 初始条件 → 计算顺序 +3. **应用**:斐波那契、背包、五子棋、最优合并、TSP… +4. **函数逼近**:DQN 可以看作 DP 表格的函数化近似 + +--- + +如果你愿意,我可以画一张图,把 **DP 子问题依赖、表格存储和状态转移** 直观展示出来,让你一眼理解 DP 的思路。 + +你希望我画吗? + + + 是的,动态规划(Dynamic Programming,简称DP)确实可以被视为一种“优化的枚举”或“带记忆化的穷举搜索”,它通过避免重复计算来高效地解决具有重叠子问题和最优子结构的问题。至于你提到的“小情况不是最优解,大情况一定不是最优解”,这本质上是在描述DP中最优子结构(Optimal Substructure)的核心性质:如果子问题(小情况)的解不是最优的,那么由这些子问题构建的整体问题(大情况)的解也一定不会是最优。下面我一步步解释这个概念,并用例子说明如何理解和应用它。 ### 1. **动态规划是什么?为什么说是“优化的枚举”?** @@ -138,4 +292,221 @@ - 实际操作中,可以先根据问题特征(求最优、能分解)尝试DP设计状态和转移方程。如果结果不对,检查是否违反了最优子结构。 - 如果你有具体问题(比如某个算法题),可以分享,我帮你分析是否适合DP、如何定义状态,或者是否需要证明最优子结构! -你是否有某个具体问题想深入探讨?比如某个DP题的推导或实现? \ No newline at end of file +你是否有某个具体问题想深入探讨?比如某个DP题的推导或实现? + +--- + +好的,我给你列一些经典的动态规划题目,覆盖不同类型的 DP,让你尝试自己想思路。**我不会给代码,只描述题目**。 + +--- + +## **1️⃣ 线性 DP(数组/序列相关)** + +1. **斐波那契数列** + + * 求第 n 个斐波那契数。 + * 思路提示:每个数依赖前两个数。 + +2. **最长递增子序列(LIS)** + + * 给定一个数组,求最长的严格递增子序列长度。 + * 思路提示:状态可以是“以 i 结尾的最长递增序列长度”。 + +3. **爬楼梯问题** + + * 每次可以爬 1 或 2 级台阶,求爬到第 n 级的方法总数。 + * 思路提示:状态可以是“到第 i 级的总方法数”。 + +--- + +## **2️⃣ 区间 DP** + +4. **矩阵连乘问题** + + * 给定矩阵序列,求最少乘法次数。 + * 思路提示:状态可以是“从 i 到 j 的最少乘法次数”。 + +5. **石子合并问题** + + * 给 n 堆石子,每次只能合并相邻两堆,求最小合并代价。 + * 思路提示:状态可以是“合并 i 到 j 的最小代价”。 + +--- + +## **3️⃣ 背包类 DP** + +6. **0-1 背包** + + * 给 n 件物品,每件重量 w\_i、价值 v\_i,背包容量为 W,求最大价值。 + * 思路提示:状态可以是“前 i 件物品,容量为 j 的最大价值”。 + +7. **完全背包** + + * 每件物品可以重复使用,求最大价值。 + * 思路提示:和 0-1 背包类似,但允许多次选同一件物品。 + +--- + +## **4️⃣ 树/图形 DP** + +8. **树上最大独立集** + + * 给一棵树,每个节点有权值,求一个集合,使得没有两个节点相连且权值和最大。 + * 思路提示:状态可以是“当前节点选/不选的最大权值”。 + +9. **迷宫/网格最短路径** + + * 给一个 m×n 网格,每格有权重,求从左上到右下的最小路径和,只能向右或向下走。 + * 思路提示:状态可以是“到 (i,j) 的最小路径和”。 + +--- + +## **5️⃣ 组合/位掩码 DP(小规模组合问题)** + +10. **旅行商问题(TSP)** + + * 给 n 个城市和距离矩阵,求访问所有城市一次并回到起点的最短路径。 + * 思路提示:状态可以是“当前在城市 i,已访问城市集合 S 的最短路径长度”。 + +11. **数位 DP** + + * 给一个数字范围 \[L,R],求满足某些条件的数字个数。 + * 思路提示:状态可以是“当前处理的位数、是否已经小于上界、某些约束条件”。 + +--- + +如果你想,我可以帮你从这些题目里挑**3\~5 个适合练习思路、又能慢慢加深难度的题**,让你自己先想 DP 状态和转移,训练你的动态规划思维。 + +你希望我帮你挑吗? + +--- + +完全正确,你抓住了 **DQN 和经典 DP 的核心区别**。我帮你梳理得更清楚: + +--- + +### 1️⃣ 经典动态规划(DP) + +* **假设前提**: + + * 完全知道状态空间、动作空间以及状态转移和奖励/代价。 + * 可以**遍历所有可能的动作或子问题**,直接计算最优值。 + +* **做法**: + + 1. 枚举所有可能的选择(动作/子问题) + 2. 根据状态转移方程计算每个选择的结果 + 3. 用 `max{}` 或 `min{}` 选出最优解 + +* **特点**: + + * **确定性**:最终得到的结果就是最优解 + * **代价**:如果状态空间太大,枚举所有可能会爆炸 + +--- + +### 2️⃣ DQN / 强化学习 + +* **假设前提**: + + * 状态空间巨大或连续,**无法穷举** + * 只能通过“尝试(探索)+奖励反馈(回报)”来学习最优策略 + +* **做法**: + + 1. 在当前状态下随机选择动作(探索 epsilon-greedy) + 2. 执行动作,得到奖励和下一个状态 + 3. 更新 Q 网络参数,让网络输出尽量逼近 **“动作的期望回报”** + +* **特点**: + + * **近似解**:通过经验和网络泛化得到最优策略,不能保证绝对最优 + * **学习过程依赖探索**:需要足够多的训练样本才能收敛 + +--- + +### 3️⃣ 核心对比 + +| 特征 | DP | DQN | +| ---- | ------------- | ------------------ | +| 知识假设 | 完全已知状态转移和奖励 | 不完全已知,只能试验和观察 | +| 动作选择 | 枚举所有可能 → 确定最优 | 随机探索 + 奖励反馈 → 逼近最优 | +| 求解方式 | 直接计算(解析或迭代) | 近似函数逼近(梯度下降更新网络) | +| 结果性质 | 最优解 | 近似最优策略,依赖训练经验 | +| 状态空间 | 可以有限遍历 | 通常太大,用神经网络逼近 | + +--- + +### 4️⃣ 总结 + +* **DP 是全知全能**,可以通过穷尽所有可能直接得到最优解 +* **DQN 是探索学习**,依赖随机动作、奖励和梯度下降,最终学到近似最优策略 +* **本质联系**:DQN 还是在逼近 Q 表,即“函数化的动态规划”,只是 **不再枚举,而是通过经验学习** + +--- + +如果你愿意,我可以画一张 **DP 枚举 vs DQN 随机探索学习** 的对比图,让整个过程一眼就能看懂。 + +你希望我画吗? + +--- + +完全对,你理解得很到位。可以帮你梳理一下这个本质问题的原因和应对思路: + +--- + +### 1️⃣ DP 的“全能”本质 + +* DP 的核心是 **枚举所有子问题 + 利用最优子结构**。 +* 因此,只要状态空间和动作空间**不是特别大**,DP 就能保证得到**全局最优解**。 +* 优点:**确定性、可解释、最优性保证**。 + +--- + +### 2️⃣ DP 的局限:状态空间爆炸 + +* 当状态空间非常大或者连续时: + + 1. **表格存储不可行**:状态数量太多,内存占用过大 + 2. **遍历计算不可行**:每个状态都要尝试所有动作,计算量指数级增长 +* 例子: + + * 五子棋 15×15 棋盘,可能局面数 > 10¹²,无法用 DP 表存储 + * 连续控制任务(机器人手臂角度连续) → 状态无限多 + +--- + +### 3️⃣ 应对策略 + +1. **状态压缩 / 特征抽象** + + * 把状态空间降维,用关键特征表示状态 + * 例:棋盘只记录重要棋型,而不是每个格子 + +2. **函数逼近(DQN / 神经网络)** + + * 不存表格,利用神经网络逼近 Q 值函数或策略 + * 原理:把 DP 的“每个状态记忆最优值”变成“网络参数分布式记忆” + +3. **搜索 + 剪枝(Minimax / Alpha-Beta)** + + * 不用枚举所有状态,只搜索有限深度的树并剪掉不可能的分支 + +4. **近似动态规划 / Monte Carlo 方法** + + * 用采样和模拟代替全局遍历 + * 强化学习就是典型应用 + +--- + +### 4️⃣ 核心总结 + +* **DP 是全能的**,但只能处理状态空间较小的情况 +* **大状态空间问题** → 传统 DP 不可行,需要**近似方法**(DQN、函数逼近、搜索剪枝) +* 本质联系:DQN 等方法是**函数化、近似化的 DP**,用经验和网络来替代表格遍历 + +--- + +如果你愿意,我可以画一张图,把 **小状态 DP vs 大状态 DQN /近似 DP** 的概念对比出来,让你一眼就看到“全能 vs 可行性”的区别。 + +你希望我画吗? diff --git a/Algorithm/Divide&Conquer/BinarySearch.cpp b/Algorithm/Divide&Conquer/BinarySearch.cpp new file mode 100644 index 0000000..895f41e --- /dev/null +++ b/Algorithm/Divide&Conquer/BinarySearch.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +using namespace std; + +auto search(vector& arr, int start, int end, int target) -> int{ + if(start >= end) return -1; + + int mid = (start + end) / 2; + + if(target == arr[mid]) return mid; + else if(target < arr[mid]) + search(arr, start, mid, target); + else if(target > arr[mid]) + search(arr, mid + 1, end, target); + +} + +int main(){ + vector arr; + int n; + cin >> n; + arr.resize(n); + for(int i = 0; i < n; i++){ + cin >> arr[i]; + } + sort(arr.begin(), arr.end()); + int t; + cin >> t; + int pos = search(arr, 0, arr.size() - 1, t); + if(pos != -1) + cout << "Pos: " << pos + 1 << ", Num: " << arr[pos] << endl; + else + cout << "Not Found" << endl; + return 0; +} diff --git a/Algorithm/Divide&Conquer/BoardCover.cpp b/Algorithm/Divide&Conquer/BoardCover.cpp new file mode 100644 index 0000000..33db965 --- /dev/null +++ b/Algorithm/Divide&Conquer/BoardCover.cpp @@ -0,0 +1,56 @@ +#include +#include +using namespace std; + +int tile = 1; // ڱDzͬƱ + +void cover(vector>& board, int top_x, int top_y, int defect_x, int defect_y, int size) { + if (size == 2) { + for (int i = 0; i < 2; ++i) + for (int j = 0; j < 2; ++j) + if (top_x+i != defect_x || top_y+j != defect_y) + board[top_x+i][top_y+j] = tile; + tile++; + return; + } + + int mid = size/2; + int cx = top_x + mid; + int cy = top_y + mid; + + // жȱĸ + int quadrant = 0; + if (defect_x < cx && defect_y < cy) quadrant = 0; // + else if (defect_x < cx && defect_y >= cy) quadrant = 1; // + else if (defect_x >= cx && defect_y < cy) quadrant = 2; // + else quadrant = 3; // + + // ķL͹ƣ3ȱ޵Ľ + if (quadrant != 0) board[cx-1][cy-1] = tile; + if (quadrant != 1) board[cx-1][cy] = tile; + if (quadrant != 2) board[cx][cy-1] = tile; + if (quadrant != 3) board[cx][cy] = tile; + tile++; + + // ݹĸ + cover(board, top_x, top_y, quadrant==0?defect_x:cx-1, quadrant==0?defect_y:cy-1, mid); // + cover(board, top_x, top_y+mid, quadrant==1?defect_x:cx-1, quadrant==1?defect_y:cy, mid); // + cover(board, top_x+mid, top_y, quadrant==2?defect_x:cx, quadrant==2?defect_y:cy-1, mid); // + cover(board, top_x+mid, top_y+mid, quadrant==3?defect_x:cx, quadrant==3?defect_y:cy, mid); // +} + +int main() { + int k = 3; // 2^3 x 2^3 + int n = 1<> board(n, vector(n, 0)); + + int defect_x = 2, defect_y = 3; // ȱλ + cover(board, 0, 0, defect_x, defect_y, n); + + // + for (auto &row : board) { + for (auto &x : row) cout << x << "\t"; + cout << "\n"; + } +} + diff --git a/Algorithm/Divide&Conquer/GiantandGhost.cpp b/Algorithm/Divide&Conquer/GiantandGhost.cpp new file mode 100644 index 0000000..6aa9e38 --- /dev/null +++ b/Algorithm/Divide&Conquer/GiantandGhost.cpp @@ -0,0 +1,13 @@ +#include +#include +//˼· +/* +±߿ʼǾ˻ǹȴҿ +ǹʹʱ(Ǻ)һ +Ǿˣ -> ʱ뿪ʼͬľ == ֮󣬻ֳ򣬷ֱ +*/ +using namespace std; +int main(){ + + return 0; +} diff --git a/Algorithm/Divide&Conquer/MergeSort.cpp b/Algorithm/Divide&Conquer/MergeSort.cpp new file mode 100644 index 0000000..b347060 --- /dev/null +++ b/Algorithm/Divide&Conquer/MergeSort.cpp @@ -0,0 +1,54 @@ +#include +#include +using namespace std; + +void mergeSort(vector& path, int start, int end){ + if(start >= end) return; + + int mid = (end + start) / 2; + + //ݹֽ + mergeSort(path, start, mid); + mergeSort(path, mid + 1, end); + + //ϲС + vector temp; + int i = start, j = mid + 1; + + while(i <= mid && j <= end){ + if(path[i] >= path[j]){ + temp.push_back(path[j]); + j++; + } + else{ + temp.push_back(path[i]); + i++; + } + } + + while(i <= mid) temp.push_back(path[i++]); + while(j <= end) temp.push_back(path[j++]); + + //ֱӰѵõĿԭ鼴ɣ + for(int x = 0; x < temp.size(); x++){ + path[start + x] = temp[x]; + } + +} + +int main(){ + vector arr; + int n; + cin >> n; + arr.resize(n); + for(int i = 0; i < n; i++){ + cin >> arr[i]; + } + mergeSort(arr, 0, arr.size() - 1); + for(int i = 0; i < arr.size(); i++){ + if(i) cout << " "; + cout << arr[i]; + } + cout << endl; + return 0; +} diff --git a/Algorithm/Greedy/P1223 排队接水.cpp b/Algorithm/Greedy/P1223 排队接水.cpp new file mode 100644 index 0000000..e0db86f --- /dev/null +++ b/Algorithm/Greedy/P1223 排队接水.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +using namespace std; + +struct cmp{ + bool operator()(const pair& a, const pair& b) const{ + if(a.second != b.second) return a.second < b.second; + else return a.first < b.first; + } +}; +int main(){ + set, cmp> s; + int n; + cin >> n; + int tmp; + double all = 0; + for(int i = 0; i < n; i++){ + cin >> tmp; + s.emplace(make_pair(i + 1, tmp)); + } + auto x = s.begin(); + for(int i = 0; i < n; i++){ + cout << (*x).first << " "; + all += (n - i - 1) * (*x).second; + x++; + } + cout << endl; + cout << fixed << setprecision(2) << all / n << endl; + return 0; +} diff --git a/Algorithm/Greedy/P2240 【深基12.例1】部分背包问题.cpp b/Algorithm/Greedy/P2240 【深基12.例1】部分背包问题.cpp new file mode 100644 index 0000000..fd29a17 --- /dev/null +++ b/Algorithm/Greedy/P2240 【深基12.例1】部分背包问题.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +using namespace std; + +struct cmp { + bool operator()(const tuple& a, + const tuple& b) const { + return get<2>(a) > get<2>(b); // Լ۱Ƚ + } +}; + +int main() { + int n; + double t; + cin >> n >> t; + + vector> arr(n); + for (int i = 0; i < n; i++) { + double a, b; + cin >> a >> b; + arr[i] = make_tuple(a, b, b / a); // (, ֵ, λֵ) + } + + sort(arr.begin(), arr.end(), cmp()); + + double value = 0; + int p = 0; + + while (t > 0 && p < n) { + if (t >= get<0>(arr[p])) { + value += get<1>(arr[p]); // ȡƷ + t -= get<0>(arr[p]); // ȼǰ + p++; + } else { + value += t * get<2>(arr[p]); // ȡһ + break; + } + } + + cout << fixed << setprecision(2) << value << endl; + return 0; +} + diff --git a/Algorithm/Greedy/Package.cpp b/Algorithm/Greedy/Package.cpp new file mode 100644 index 0000000..e69de29 diff --git a/Algorithm/Greedy/UVa10954AddAll.cpp b/Algorithm/Greedy/UVa10954AddAll.cpp new file mode 100644 index 0000000..adc08be --- /dev/null +++ b/Algorithm/Greedy/UVa10954AddAll.cpp @@ -0,0 +1,23 @@ +#include +#include +using namespace std; +int main(){ + long long sum = 0; + priority_queue, greater> q; + int n; + cin >> n; + for(int i = 0; i < n; i++){ + int tmp; + cin >> tmp; + q.push(tmp); + } + while(q.size() > 1){ + int a = q.top();q.pop(); + int b = q.top();q.pop(); + int tmp = a + b; + sum += tmp; + q.push(tmp); + } + cout << sum << endl; + return 0; +} diff --git a/Algorithm/Greedy/UVa714Copying-Books.cpp b/Algorithm/Greedy/UVa714Copying-Books.cpp new file mode 100644 index 0000000..0a69daa --- /dev/null +++ b/Algorithm/Greedy/UVa714Copying-Books.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +//-> +//ֲ mid = (low + high) / 2 +//̰֤ Ƿڷ鷽ʽ + +using namespace std; + +bool check(const vector& a, int target, int k) { + int js = 1; // ǰ1дԱ + int temp = 0; // ǰдԱ + + for (int i = 0; i < a.size(); i++) { + if (a[i] > target) return false; // ҳ target + if (temp + a[i] > target) { // targetһдԱ + js++; + temp = a[i]; + } else { + temp += a[i]; + } + } + + return js <= k; // Ƿ +} + +int main(){ + int n, k; + cin >> n >> k; + vector arr; + arr.resize(n); + + int max = 0; + int sum = 0; + for(int i = 0; i < n; i++){ + cin >> arr[i]; + if(max < arr[i]) max = arr[i]; + sum += arr[i]; + } + + int left = max; + int right = sum; + int result = right; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (check(arr, mid, k)) { + result = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + cout << result << endl; + + return 0; +}