vault backup: 2026-06-10 11:19:43

This commit is contained in:
2026-06-10 11:19:43 +08:00
parent 788576ec4b
commit b9bc08ab2f
6 changed files with 391 additions and 209 deletions
+69 -39
View File
@@ -65,13 +65,21 @@ flowchart TD
归并排序是分治法最经典的应用之一,时间复杂度 **O(n logn)**。 归并排序是分治法最经典的应用之一,时间复杂度 **O(n logn)**。
```plaintext ```cpp
function mergeSort(A, left, right): #include <vector>
if left < right: using namespace std;
mid = (left + right) / 2
mergeSort(A, left, mid) // 分解:排序左半部分 // merge函数省略,此处仅展示归并排序的分治骨架
mergeSort(A, mid+1, right) // 分解:排序右半部分 // void merge(vector<int>& A, int left, int mid, int right) { ... }
merge(A, left, mid, right) // 合并:合并两个有序子数组
void mergeSort(vector<int>& A, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(A, left, mid); // 分解:排序左半部分
mergeSort(A, mid + 1, right); // 分解:排序右半部分
// merge(A, left, mid, right); // 合并:合并两个有序子数组
}
}
``` ```
- **分解**:将数组从中间分为两半 - **分解**:将数组从中间分为两半
@@ -95,28 +103,42 @@ function mergeSort(A, left, right):
- 每组2个元素:组内比较1次,再与当前最大、最小各比较1次 - 每组2个元素:组内比较1次,再与当前最大、最小各比较1次
- 比较次数从 **2(n-1)** 降至约 **3n/2** - 比较次数从 **2(n-1)** 降至约 **3n/2**
```plaintext ```cpp
function findMaxMin(A, n): #include <vector>
// 初始化 #include <utility>
if n is odd: using namespace std;
maxVal = minVal = A[1]
start = 2
else:
if A[1] < A[2]:
minVal = A[1], maxVal = A[2]
else:
minVal = A[2], maxVal = A[1]
start = 3
// 两两比较 // 同时求最大值和最小值,比较次数约 3n/2
for i = start to n step 2: pair<int, int> findMaxMin(vector<int>& A, int n) {
if A[i] < A[i+1]: int maxVal, minVal;
smaller, larger = A[i], A[i+1] int start;
else:
smaller, larger = A[i+1], A[i] // 初始化:根据元素个数的奇偶性决定初始值
if smaller < minVal: minVal = smaller if (n % 2 == 1) {
if larger > maxVal: maxVal = larger maxVal = minVal = A[0];
return (maxVal, minVal) start = 1;
} else {
if (A[0] < A[1]) {
minVal = A[0]; maxVal = A[1];
} else {
minVal = A[1]; maxVal = A[0];
}
start = 2;
}
// 两两比较:每组内比较1次,再分别与全局最大最小比较
for (int i = start; i < n; i += 2) {
int smaller, larger;
if (A[i] < A[i + 1]) {
smaller = A[i]; larger = A[i + 1];
} else {
smaller = A[i + 1]; larger = A[i];
}
if (smaller < minVal) minVal = smaller;
if (larger > maxVal) maxVal = larger;
}
return {maxVal, minVal};
}
``` ```
**解释**:每次循环处理两个元素,组内比较1次确定大小,再分别与全局最大最小比较最多2次,所以每组最多3次比较。总比较次数约为 3(n-1)/2。 **解释**:每次循环处理两个元素,组内比较1次确定大小,再分别与全局最大最小比较最多2次,所以每组最多3次比较。总比较次数约为 3(n-1)/2。
@@ -129,17 +151,25 @@ function findMaxMin(A, n):
2. 只有当某个元素**大于当前最大值**时,才更新第二大值 2. 只有当某个元素**大于当前最大值**时,才更新第二大值
3. 一次遍历即可完成 3. 一次遍历即可完成
```plaintext ```cpp
function findSecondMax(A, n): #include <vector>
maxVal = A[1] #include <climits>
secondMax = -infinity using namespace std;
for i = 2 to n:
if A[i] > maxVal: // 一次遍历找第二大的数,时间O(n)
secondMax = maxVal // 原来的最大变为第二 int findSecondMax(vector<int>& A, int n) {
maxVal = A[i] // 更新最大 int maxVal = A[0];
else if A[i] > secondMax: int secondMax = INT_MIN;
secondMax = A[i] // 更新第二 for (int i = 1; i < n; i++) {
return secondMax if (A[i] > maxVal) {
secondMax = maxVal; // 原来的最大变为第二
maxVal = A[i]; // 更新最大
} else if (A[i] > secondMax) {
secondMax = A[i]; // 更新第二
}
}
return secondMax;
}
``` ```
#### 4.5 伪币问题 #### 4.5 伪币问题
@@ -76,17 +76,24 @@ $$dp[i] = \max(arr[i], \ dp[i-1] + arr[i])$$
**含义**:以第 i 个元素结尾的最大子数组和,要么是从头开始(只取 arr[i]),要么是接在之前的最大子数组后面。 **含义**:以第 i 个元素结尾的最大子数组和,要么是从头开始(只取 arr[i]),要么是接在之前的最大子数组后面。
```plaintext ```cpp
function maxSubarraySum(arr, n): #include <vector>
dp = array of size n #include <algorithm>
dp[0] = arr[0] using namespace std;
maxSum = dp[0]
for i = 1 to n-1: int maxSubarraySum(vector<int>& arr) {
dp[i] = max(arr[i], dp[i-1] + arr[i]) int n = arr.size();
maxSum = max(maxSum, dp[i]) vector<int> dp(n);
dp[0] = arr[0];
int maxSum = dp[0];
return maxSum for (int i = 1; i < n; i++) {
dp[i] = max(arr[i], dp[i-1] + arr[i]); // 从头开始 or 接在前面
maxSum = max(maxSum, dp[i]);
}
return maxSum;
}
``` ```
**解释**:对每个位置 i,我们做一个决策——是从当前位置重新开始子数组,还是接在前面的子数组后面。选择较大的那个作为 dp[i]。最后取所有 dp[i] 的最大值。 **解释**:对每个位置 i,我们做一个决策——是从当前位置重新开始子数组,还是接在前面的子数组后面。选择较大的那个作为 dp[i]。最后取所有 dp[i] 的最大值。
@@ -104,25 +111,30 @@ function maxSubarraySum(arr, n):
$$dp[i][j] = \max(dp[i-1][j], \ dp[i][j-1]) + matrix[i][j]$$ $$dp[i][j] = \max(dp[i-1][j], \ dp[i][j-1]) + matrix[i][j]$$
```plaintext ```cpp
function maxMatrixPath(matrix, n): #include <vector>
dp = n x n matrix #include <algorithm>
dp[0][0] = matrix[0][0] using namespace std;
int maxMatrixPath(vector<vector<int>>& matrix, int n) {
vector<vector<int>> dp(n, vector<int>(n));
dp[0][0] = matrix[0][0];
// 填第一行(只能从左边来) // 填第一行(只能从左边来)
for j = 1 to n-1: for (int j = 1; j < n; j++)
dp[0][j] = dp[0][j-1] + matrix[0][j] dp[0][j] = dp[0][j-1] + matrix[0][j];
// 填第一列(只能从上面来) // 填第一列(只能从上面来)
for i = 1 to n-1: for (int i = 1; i < n; i++)
dp[i][0] = dp[i-1][0] + matrix[i][0] dp[i][0] = dp[i-1][0] + matrix[i][0];
// 填其余位置 // 填其余位置
for i = 1 to n-1: for (int i = 1; i < n; i++)
for j = 1 to n-1: for (int j = 1; j < n; j++)
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + matrix[i][j] dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + matrix[i][j];
return dp[n-1][n-1] return dp[n-1][n-1];
}
``` ```
**解释**:到达位置 (i,j) 只能从上方 (i-1,j) 或左方 (i,j-1) 来。取两者中较大的路径和,加上当前位置的值。边界条件是第一行和第一列只有一条路径。 **解释**:到达位置 (i,j) 只能从上方 (i-1,j) 或左方 (i,j-1) 来。取两者中较大的路径和,加上当前位置的值。边界条件是第一行和第一列只有一条路径。
@@ -137,15 +149,19 @@ $$f(n) = f(n-1) + f(n-2)$$
**解释**:到达第 n 阶的方法数 = 从第 n-1 阶走1步 + 从第 n-2 阶走2步。这是一个变体的 Fibonacci 数列。 **解释**:到达第 n 阶的方法数 = 从第 n-1 阶走1步 + 从第 n-2 阶走2步。这是一个变体的 Fibonacci 数列。
```plaintext ```cpp
function climbStairs(n): #include <vector>
if n <= 2: return n using namespace std;
dp = array of size n+1
dp[1] = 1 int climbStairs(int n) {
dp[2] = 2 if (n <= 2) return n;
for i = 3 to n: vector<int> dp(n + 1);
dp[i] = dp[i-1] + dp[i-2] dp[1] = 1;
return dp[n] dp[2] = 2;
for (int i = 3; i <= n; i++)
dp[i] = dp[i-1] + dp[i-2];
return dp[n];
}
``` ```
> [!question] 爬楼梯和Fibonacci有什么关系? > [!question] 爬楼梯和Fibonacci有什么关系?
@@ -155,15 +171,19 @@ function climbStairs(n):
用动态规划可以将 Fibonacci 从 O(2ⁿ) 优化到 O(n): 用动态规划可以将 Fibonacci 从 O(2ⁿ) 优化到 O(n):
```plaintext ```cpp
function fibDP(n): #include <vector>
if n <= 1: return n using namespace std;
dp = array of size n+1
dp[0] = 0 int fibDP(int n) {
dp[1] = 1 if (n <= 1) return n;
for i = 2 to n: vector<int> dp(n + 1);
dp[i] = dp[i-1] + dp[i-2] dp[0] = 0;
return dp[n] dp[1] = 1;
for (int i = 2; i <= n; i++)
dp[i] = dp[i-1] + dp[i-2];
return dp[n];
}
``` ```
> [!tip] 进一步优化空间 > [!tip] 进一步优化空间
@@ -179,18 +199,26 @@ function fibDP(n):
$$dp[i][j] = \begin{cases} dp[i-1][j-1] + 1 & \text{if } X[i] = Y[j] \\ \max(dp[i-1][j], \ dp[i][j-1]) & \text{if } X[i] \neq Y[j] \end{cases}$$ $$dp[i][j] = \begin{cases} dp[i-1][j-1] + 1 & \text{if } X[i] = Y[j] \\ \max(dp[i-1][j], \ dp[i][j-1]) & \text{if } X[i] \neq Y[j] \end{cases}$$
```plaintext ```cpp
function LCS(X, Y, m, n): #include <vector>
dp = (m+1) x (n+1) matrix, all zeros #include <string>
#include <algorithm>
using namespace std;
for i = 1 to m: int LCS(string& X, string& Y, int m, int n) {
for j = 1 to n: vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
if X[i] == Y[j]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n] for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (X[i-1] == Y[j-1])
dp[i][j] = dp[i-1][j-1] + 1; // 字符匹配,LCS长度+1
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]); // 取两种情况的较大值
}
}
return dp[m][n];
}
``` ```
**解释**:如果当前字符匹配,则 LCS 长度等于去掉这两个字符后的 LCS 长度加 1;如果不匹配,则取"去掉 X 的当前字符"和"去掉 Y 的当前字符"两种情况的较大值。 **解释**:如果当前字符匹配,则 LCS 长度等于去掉这两个字符后的 LCS 长度加 1;如果不匹配,则取"去掉 X 的当前字符"和"去掉 Y 的当前字符"两种情况的较大值。
+84 -43
View File
@@ -78,18 +78,27 @@ flowchart TD
### 五、回溯法一般框架 ### 五、回溯法一般框架
```plaintext ```cpp
void backtrack(int t) { #include <vector>
using namespace std;
// 回溯法框架
// t: 当前搜索深度(第几个决策)
// n: 问题规模(总共需要做的决策数)
// x: 当前解向量
void backtrack(int t, int n, vector<int>& x) {
if (t > n) if (t > n)
output(x); // 到达叶节点,输出解 // output(x); // 到达叶节点,输出解
else { return;
for (int i = f(n,t); i <= g(n,t); i++) { // f(n,t) 和 g(n,t) 为第t个位置可选值的范围
x[t] = h(i); // 第t个位置尝试第i种选择 // h(i) 将第i个候选值赋给x[t]
if (constraint(t) && bound(t)) // constraint(t) 为约束函数,bound(t) 为限界函数
backtrack(t+1); // 满足约束,继续深入 for (int i = 1; i <= n; i++) {
x[t] = i; // 第t个位置尝试第i种选择
if (/* constraint(t) && */ /* bound(t) */ true)
backtrack(t + 1, n, x); // 满足约束,继续深入
// 否则剪枝,回退(x[t]会被下次循环覆盖) // 否则剪枝,回退(x[t]会被下次循环覆盖)
} }
}
} }
``` ```
@@ -116,23 +125,40 @@ void backtrack(int t) {
**时间复杂度**:最坏 O(mⁿ),四色问题为 O(4ⁿ) **时间复杂度**:最坏 O(mⁿ),四色问题为 O(4ⁿ)
```plaintext ```cpp
function graphColoring(graph, m, colors, vertex): #include <vector>
if vertex == n: #include <iostream>
printSolution(colors) // 所有顶点着色完成 using namespace std;
return
for c = 1 to m: // 判断将 vertex 着色为 colors[vertex] 是否安全
colors[vertex] = c // 尝试颜色c bool isSafe(vector<vector<int>>& graph, vector<int>& colors, int vertex) {
if isSafe(graph, colors, vertex): int n = graph.size();
graphColoring(graph, m, colors, vertex + 1) for (int v = 0; v < n; v++) {
colors[vertex] = 0 // 回溯 if (graph[vertex][v] && colors[v] == colors[vertex])
return false; // 相邻顶点颜色相同,不安全
}
return true;
}
function isSafe(graph, colors, vertex): // 图着色回溯算法
for each neighbor v of vertex: // graph: 邻接矩阵, m: 颜色数, colors: 着色方案, vertex: 当前处理的顶点
if colors[v] == colors[vertex]: void graphColoring(vector<vector<int>>& graph, int m, vector<int>& colors, int vertex) {
return false int n = graph.size();
return true if (vertex == n) {
// 所有顶点着色完成,输出解
for (int i = 0; i < n; i++)
cout << colors[i] << " ";
cout << endl;
return;
}
for (int c = 1; c <= m; c++) {
colors[vertex] = c; // 尝试颜色c
if (isSafe(graph, colors, vertex))
graphColoring(graph, m, colors, vertex + 1);
colors[vertex] = 0; // 回溯
}
}
``` ```
```mermaid ```mermaid
@@ -173,29 +199,44 @@ flowchart TD
- 检查列冲突和对角线冲突 - 检查列冲突和对角线冲突
- 冲突则剪枝 - 冲突则剪枝
```plaintext ```cpp
function solveNQueens(board, row, n): #include <vector>
if row == n: #include <iostream>
printBoard(board) using namespace std;
return
for col = 0 to n-1: // 判断在 (row, col) 放置皇后是否安全
if isSafe(board, row, col, n): bool isSafe(vector<string>& board, int row, int col, int n) {
board[row][col] = "Q" // 放置皇后
solveNQueens(board, row + 1, n)
board[row][col] = "." // 回溯
function isSafe(board, row, col, n):
// 检查同一列 // 检查同一列
for i = 0 to row-1: for (int i = 0; i < row; i++)
if board[i][col] == "Q": return false if (board[i][col] == 'Q') return false;
// 检查左上对角线 // 检查左上对角线
for i = row-1, j = col-1; i >= 0 && j >= 0; i--, j--: for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--)
if board[i][j] == "Q": return false if (board[i][j] == 'Q') return false;
// 检查右上对角线 // 检查右上对角线
for i = row-1, j = col+1; i >= 0 && j < n; i--, j++: for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++)
if board[i][j] == "Q": return false if (board[i][j] == 'Q') return false;
return true return true;
}
// N皇后回溯算法
// board: 棋盘, row: 当前处理的行, n: 棋盘大小
void solveNQueens(vector<string>& board, int row, int n) {
if (row == n) {
// 输出一个解
for (int i = 0; i < n; i++)
cout << board[i] << endl;
cout << endl;
return;
}
for (int col = 0; col < n; col++) {
if (isSafe(board, row, col, n)) {
board[row][col] = 'Q'; // 放置皇后
solveNQueens(board, row + 1, n);
board[row][col] = '.'; // 回溯
}
}
}
``` ```
**解释**:N皇后是经典的回溯应用。由于每行只放一个皇后,解空间是排列树。剪枝函数检查列冲突和两条对角线冲突,大幅减少搜索空间。 **解释**:N皇后是经典的回溯应用。由于每行只放一个皇后,解空间是排列树。剪枝函数检查列冲突和两条对角线冲突,大幅减少搜索空间。
@@ -21,28 +21,52 @@ DFS 沿着一条路径**尽可能深地**搜索,走不通再回退。
**实现方式**:使用**栈**(Stack)或**递归调用栈** **实现方式**:使用**栈**(Stack)或**递归调用栈**
```plaintext ```cpp
function DFS(graph, start): #include <vector>
visited = set() #include <stack>
stack = [start] #include <iostream>
using namespace std;
while stack is not empty: // DFS(栈实现)
node = stack.pop() // graph: 邻接表, start: 起始顶点
if node not in visited: void DFS(vector<vector<int>>& graph, int start) {
visited.add(node) int n = graph.size();
for neighbor in graph[node]: vector<bool> visited(n, false);
if neighbor not in visited: stack<int> st;
stack.push(neighbor) st.push(start);
while (!st.empty()) {
int node = st.top();
st.pop();
if (!visited[node]) {
visited[node] = true;
cout << node << " ";
for (int neighbor : graph[node]) {
if (!visited[neighbor])
st.push(neighbor);
}
}
}
}
``` ```
递归版本更直观: 递归版本更直观:
```plaintext ```cpp
function DFS_recursive(graph, node, visited): #include <vector>
visited.add(node) #include <iostream>
for neighbor in graph[node]: using namespace std;
if neighbor not in visited:
DFS_recursive(graph, neighbor, visited) // DFS(递归版本)
// graph: 邻接表, node: 当前顶点, visited: 访问标记数组
void DFS_recursive(vector<vector<int>>& graph, int node, vector<bool>& visited) {
visited[node] = true;
cout << node << " ";
for (int neighbor : graph[node]) {
if (!visited[neighbor])
DFS_recursive(graph, neighbor, visited);
}
}
``` ```
### 二、BFS(广度优先搜索) ### 二、BFS(广度优先搜索)
@@ -51,18 +75,33 @@ BFS **按层遍历**,先访问所有距离为1的节点,再访问距离为2
**实现方式**:使用**队列**(Queue) **实现方式**:使用**队列**(Queue)
```plaintext ```cpp
function BFS(graph, start): #include <vector>
visited = set() #include <queue>
queue = [start] #include <iostream>
visited.add(start) using namespace std;
while queue is not empty: // BFS(队列实现)
node = queue.dequeue() // graph: 邻接表, start: 起始顶点
for neighbor in graph[node]: void BFS(vector<vector<int>>& graph, int start) {
if neighbor not in visited: int n = graph.size();
visited.add(neighbor) vector<bool> visited(n, false);
queue.enqueue(neighbor) queue<int> q;
q.push(start);
visited[start] = true;
while (!q.empty()) {
int node = q.front();
q.pop();
cout << node << " ";
for (int neighbor : graph[node]) {
if (!visited[neighbor]) {
visited[neighbor] = true;
q.push(neighbor);
}
}
}
}
``` ```
### 三、BFS vs DFS 对比 ### 三、BFS vs DFS 对比
@@ -112,13 +151,17 @@ flowchart TD
**原理**:gcd(a, b) = gcd(b, a mod b),直到 b = 0 时 a 即为答案。 **原理**:gcd(a, b) = gcd(b, a mod b),直到 b = 0 时 a 即为答案。
```plaintext ```cpp
function gcd(a, b): // 欧几里得算法(辗转相除法)
while b != 0: // 求两个正整数的最大公约数
temp = b int gcd(int a, int b) {
b = a mod b while (b != 0) {
a = temp int temp = b;
return a b = a % b;
a = temp;
}
return a;
}
``` ```
**示例**:gcd(2146, 8100) **示例**:gcd(2146, 8100)
@@ -48,14 +48,20 @@ create time: 2026-06-10 11:11
2. **流程图**:图形化表示,直观清晰。注意:**菱形表示判断/选择** 2. **流程图**:图形化表示,直观清晰。注意:**菱形表示判断/选择**
3. **伪代码**:介于自然语言和编程语言之间,兼顾可读性和精确性 3. **伪代码**:介于自然语言和编程语言之间,兼顾可读性和精确性
```plaintext ```cpp
// 伪代码示例:查找数组中的最小值 #include <vector>
function findMin(A, n): using namespace std;
minVal = A[1]
for i = 2 to n: // 查找数组中的最小值
if A[i] < minVal: int findMin(vector<int>& A, int n) {
minVal = A[i] int minVal = A[0];
return minVal for (int i = 1; i < n; i++) {
if (A[i] < minVal) {
minVal = A[i];
}
}
return minVal;
}
``` ```
> [!tip] 流程图关键符号 > [!tip] 流程图关键符号
@@ -161,17 +167,18 @@ T(0) = 0, T(1) = 0
- 每次返回,从栈顶弹出恢复现场 - 每次返回,从栈顶弹出恢复现场
- **DFS(深度优先搜索)** 也使用栈实现 - **DFS(深度优先搜索)** 也使用栈实现
```plaintext ```cpp
// Fibonacci的迭代版本(使用栈模拟的思想,但实际是循环) // Fibonacci的迭代版本(时间O(n),空间O(1))
function fibIterative(n): int fibIterative(int n) {
if n <= 1: return n if (n <= 1) return n;
prev = 0 int prev = 0, curr = 1;
curr = 1 for (int i = 2; i <= n; i++) {
for i = 2 to n: int next = prev + curr;
next = prev + curr prev = curr;
prev = curr curr = next;
curr = next }
return curr return curr;
}
``` ```
迭代版本将时间复杂度从 O(2ⁿ) 降至 O(n),空间复杂度从 O(n)(递归栈)降至 O(1)。 迭代版本将时间复杂度从 O(2ⁿ) 降至 O(n),空间复杂度从 O(n)(递归栈)降至 O(1)。
@@ -58,18 +58,35 @@ flowchart TD
**贪心策略**:**按结束时间从早到晚排序**,优先选择结束时间早的活动。 **贪心策略**:**按结束时间从早到晚排序**,优先选择结束时间早的活动。
```plaintext ```cpp
function activitySelection(activities): #include <vector>
sort activities by finish time #include <algorithm>
selected = [activities[1]] // 选择第一个结束最早的 using namespace std;
lastFinish = activities[1].finish
for i = 2 to n: struct Activity {
if activities[i].start >= lastFinish: int start, finish;
selected.append(activities[i]) };
lastFinish = activities[i].finish
return selected // 活动选择问题:按结束时间贪心选择最多的互不冲突活动
vector<int> activitySelection(vector<Activity>& activities) {
// 按结束时间从早到晚排序
sort(activities.begin(), activities.end(),
[](const Activity& a, const Activity& b) {
return a.finish < b.finish;
});
vector<int> selected;
selected.push_back(0); // 选择第一个结束最早的
int lastFinish = activities[0].finish;
for (int i = 1; i < (int)activities.size(); i++) {
if (activities[i].start >= lastFinish) {
selected.push_back(i); // 记录被选中的活动下标
lastFinish = activities[i].finish;
}
}
return selected;
}
``` ```
**解释**:选择结束时间最早的活动,可以为后续活动留出最多的时间空间。每选择一个活动,就排除与其冲突的活动,然后继续在剩余活动中选结束最早的。 **解释**:选择结束时间最早的活动,可以为后续活动留出最多的时间空间。每选择一个活动,就排除与其冲突的活动,然后继续在剩余活动中选结束最早的。
@@ -83,21 +100,37 @@ function activitySelection(activities):
**贪心策略**:按**价值密度**(vᵢ/wᵢ)从高到低排序,优先取价值密度最高的物品。 **贪心策略**:按**价值密度**(vᵢ/wᵢ)从高到低排序,优先取价值密度最高的物品。
```plaintext ```cpp
function fractionalKnapsack(items, W): #include <vector>
sort items by value/weight ratio descending #include <algorithm>
totalValue = 0 using namespace std;
remaining = W
for each item in items: struct Item {
if item.weight <= remaining: double weight, value;
totalValue += item.value };
remaining -= item.weight
else:
totalValue += item.value * (remaining / item.weight)
break
return totalValue // 分数背包问题:按价值密度贪心,可取物品的一部分
double fractionalKnapsack(vector<Item>& items, double W) {
// 按价值密度(value/weight)从高到低排序
sort(items.begin(), items.end(),
[](const Item& a, const Item& b) {
return (a.value / a.weight) > (b.value / b.weight);
});
double totalValue = 0;
double remaining = W;
for (const auto& item : items) {
if (item.weight <= remaining) {
totalValue += item.value; // 整个物品装入
remaining -= item.weight;
} else {
totalValue += item.value * (remaining / item.weight); // 装入一部分
break;
}
}
return totalValue;
}
``` ```
**解释**:因为可以取物品的一部分,所以优先取"性价比"最高的物品一定能得到最优解。剩余容量不够装整个物品时,取一部分即可。 **解释**:因为可以取物品的一部分,所以优先取"性价比"最高的物品一定能得到最优解。剩余容量不够装整个物品时,取一部分即可。