当前位置: 首页 > news >正文

竞赛常考的知识点大总结(五)动态规划

DP问题的性质

动态规划(Dynamic Programming,DP)是指在解决动态规划问题时所依赖的一些基本特征和规律。动态规划是一种将复杂问题分解为更小子问题来解决的方法,它适用于具有重叠子问题和最优子结构性质的问题。动态规划问题通常具有以下特点:

特点:

1.最优子结构:问题的最优解包含其子问题的最优解。也就是说,一个问题的最优解可以从其子问题的最优解构造而来。

2.重叠子问题:在问题的求解过程中,相同的子问题会被多次计算。动态规划通过存储这些子问题的解来避免重复计算。

3.无后效性:一旦某个阶段的状态确定之后,它就不会再受之后阶段的决策影响。即一个阶段的状态一旦确定,就不会再改变。

4.状态转移方程:动态规划问题通常可以通过状态转移方程来描述问题的递推关系,即如何从一个或多个子问题的解来得到当前问题的解。

常见用法:

1.背包问题:如0/1背包问题、完全背包问题等。

2.最长公共子序列:如编辑距离、最长公共子序列等。

3.最短路径问题:如Floyd-Warshall算法、Dijkstra算法等。

4.计数问题:如硬币找零问题、计数问题等。

5.序列问题:如最长上升子序列、最长回文子序列等。

经典C语言例题:

题目: 使用动态规划解决0/1背包问题。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include<string.h>// 计算最大值
int max(int a, int b) {return (a > b) ? a : b;
}
// 定义背包问题的结构体
typedef struct {int capacity; // 背包容量int* weights; // 物品重量数组int* values; // 物品价值数组int n; // 物品种类数
} Knapsack;// 创建背包问题实例
Knapsack* createKnapsack(int capacity, int* weights, int* values, int n) {Knapsack* knapsack = (Knapsack*)malloc(sizeof(Knapsack));knapsack->capacity = capacity;knapsack->weights = weights;knapsack->values = values;knapsack->n = n;return knapsack;
}// 计算最大价值
int knapsack_(Knapsack* knapsack) {int** dp = (int**)malloc((knapsack->n + 1) * sizeof(int*));for (int i = 0; i <= knapsack->n; i++) {dp[i] = (int*)malloc((knapsack->capacity + 1) * sizeof(int));memset(dp[i], 0, (knapsack->capacity + 1) * sizeof(int));}for (int i = 1; i <= knapsack->n; i++) {for (int w = 1; w <= knapsack->capacity; w++) {if (knapsack->weights[i - 1] <= w) {dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - knapsack->weights[i - 1]] + knapsack->values[i - 1]);} else {dp[i][w] = dp[i - 1][w];}}}int maxValue = dp[knapsack->n][knapsack->capacity];for (int i = 0; i <= knapsack->n; i++) {free(dp[i]);}free(dp);return maxValue;
}int main() {int capacity = 50;int weights[] = {10, 20, 30};int values[] = {60, 100, 120};int n = sizeof(weights) / sizeof(weights[0]);Knapsack* knapsack = createKnapsack(capacity, weights, values, n);printf("Maximum value in knapsack: %d\n", knapsack_(knapsack));free(knapsack->weights);free(knapsack->values);free(knapsack);return 0;
}

例题分析:

1.创建背包问题实例createKnapsack函数创建一个背包问题实例,包括背包容量、物品重量数组、物品价值数组和物品种类数。

2.计算最大价值knapsack函数使用动态规划方法计算背包问题的最大价值。它首先创建一个二维数组dp来存储子问题的解,然后通过两层循环遍历所有物品和所有可能的重量,计算每个子问题的解,并更新dp数组。

3.主函数:在main函数中,定义了一个背包问题实例,并调用knapsack函数计算最大价值,最后打印结果。

这个例题展示了如何在C语言中使用动态规划解决0/1背包问题。通过这个例子,可以更好地理解动态规划在解决背包问题中的应用,以及如何使用动态规划来高效地解决具有重叠子问题和最优子结构性质的问题。动态规划通过存储子问题的解来避免重复计算,从而提高了算法的效率。

编码方法(记忆化递归、递推)

编码方法通常指的是在编程中用于解决问题的特定技巧或策略。在动态规划(DP)问题中,编码方法主要涉及记忆化递归和递推两种技术。

记忆化递归(Memoization)

记忆化递归是一种优化递归调用的技术,它通过存储已经计算过的子问题的解来避免重复计算,从而减少计算时间。记忆化通常使用一个数组或哈希表来存储子问题的解。

特点:

1.避免重复计算:通过存储子问题的解,避免了对相同子问题的重复计算。

2.提高效率:减少了不必要的计算,提高了算法的效率。

3.空间换时间:需要额外的空间来存储子问题的解。

常见用法:

1.动态规划问题:在解决具有重叠子问题的动态规划问题时,使用记忆化递归可以显著提高效率。

2.计算斐波那契数列:使用记忆化递归可以高效地计算斐波那契数列的值。

经典C语言例题:

题目: 使用记忆化递归计算斐波那契数列的第n项。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
// 计算斐波那契数列的第n项,使用记忆化递归
int fibonacci(int n, int* memo) {if (n <= 1) {return n;}// 检查是否已经计算过if (memo[n] != -1) {return memo[n];}// 计算并存储结果memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);return memo[n];
}int main() {int n = 10;int* memo = (int*)malloc((n + 1) * sizeof(int));memset(memo, -1, (n + 1) * sizeof(int));printf("Fibonacci number at position %d is %d\n", n, fibonacci(n, memo));free(memo);return 0;
}

例题分析:

1.记忆化递归函数fibonacci函数接受一个整数n和一个整数数组memo作为参数。memo数组用于存储已经计算过的斐波那契数列的值。

2.递归计算:函数首先检查n是否小于或等于1,如果是,则直接返回n。否则,函数检查memo[n]是否已经被计算过,如果是,则直接返回memo[n]

3.计算并存储结果:如果memo[n]没有被计算过,则递归地调用fibonacci函数计算n-1n-2的斐波那契数,并将结果存储在memo[n]中。

4.主函数:在main函数中,定义了一个整数n和一个整数数组memo。调用fibonacci函数计算斐波那契数列的第n项,并打印结果。

这个例题展示了如何在C语言中使用记忆化递归来计算斐波那契数列的第n项。通过这个例子,可以更好地理解记忆化递归在解决动态规划问题中的应用,以及如何使用记忆化技术来提高算法的效率。记忆化递归通过存储子问题的解,避免了对相同子问题的重复计算,从而减少了计算时间,提高了算法的效率。

递推(Tabulation)

递推是一种自底向上的动态规划技术,它从最基础的情况开始,逐步构建出整个问题的解。递推通常使用一个数组来存储子问题的解,并通过迭代的方式计算出最终的解。

特点:

1.自底向上:从最基础的情况开始,逐步构建出整个问题的解。

2.避免递归:不使用递归调用,而是通过迭代的方式计算出最终的解。

3.空间优化:通常只需要一个一维数组来存储子问题的解。

常见用法:

1.动态规划问题:在解决具有重叠子问题的动态规划问题时,使用递推可以避免递归调用带来的额外开销。

2.计算斐波那契数列:使用递推可以高效地计算斐波那契数列的值。

经典C语言例题:

题目: 使用递推计算斐波那契数列的第n项。

示例代码:

#include <stdio.h>// 计算斐波那契数列的第n项,使用递推
int fibonacci(int n) {int fib[n + 1];fib[0] = 0;fib[1] = 1;for (int i = 2; i <= n; i++) {fib[i] = fib[i - 1] + fib[i - 2];}return fib[n];
}int main() {int n = 10;printf("Fibonacci number at position %d is %d\n", n, fibonacci(n));return 0;
}

例题分析:

1.递推函数fibonacci函数接受一个整数n作为参数,并计算斐波那契数列的第n项。

2.初始化数组:函数首先初始化一个数组fib,并设置fib[0]为0,fib[1]为1。

3.迭代计算:函数通过一个循环从i = 2开始,到i = n结束,计算fib[i]的值,并将其存储在数组中。

4.返回结果:函数最后返回fib[n]的值,即斐波那契数列的第n项。

这个例题展示了如何在C语言中使用递推技术来计算斐波那契数列的第n项。通过这个例子,可以更好地理解递推在解决动态规划问题中的应用,以及如何使用迭代的方式来计算问题的解。递推通过自底向上的方式,逐步构建出整个问题的解,避免了递归调用带来的额外开销,提高了算法的效率。

滚动数组

滚动数组(Rolling Array)是一种在动态规划问题中使用的优化技术,它用于减少存储空间的使用。滚动数组通过重用数组空间来存储不同阶段的子问题解,从而避免了为每个阶段都创建一个新数组。

特点:

1.空间优化:滚动数组通过重用数组空间来减少存储空间的使用,通常将数组大小减少到常数级别。

2.避免重复创建数组:在动态规划问题中,每个阶段的子问题解通常只依赖于前一个阶段的解,因此可以重用数组空间。

3.易于实现:滚动数组的实现通常很简单,只需要在计算下一个阶段的解时覆盖前一个阶段的解即可。

4.适用范围:滚动数组适用于那些子问题解只依赖于前一个阶段解的动态规划问题。

常见用法:

1.动态规划问题:在解决具有重叠子问题的动态规划问题时,使用滚动数组可以显著减少空间复杂度。

2.计算斐波那契数列:使用滚动数组可以高效地计算斐波那契数列的值。

经典C语言例题:

题目: 使用滚动数组计算斐波那契数列的第n项。

示例代码:

#include <stdio.h>// 计算斐波那契数列的第n项,使用滚动数组
int fibonacci(int n) {int a = 0, b = 1, c;if (n == 0) {return a;}for (int i = 2; i <= n; i++) {c = a + b;a = b;b = c;}return b;
}int main() {int n = 10;printf("Fibonacci number at position %d is %d\n", n, fibonacci(n));return 0;
}

例题分析:

1.滚动数组:在fibonacci函数中,我们只使用了三个变量abc来存储斐波那契数列的当前项、前一项和下一项的值。

2.计算斐波那契数列:函数首先初始化a为0,b为1。然后,通过一个循环从i = 2开始,到i = n结束,计算斐波那契数列的第n项。在每次循环中,我们计算下一项的值c,然后更新ab的值,使得a始终存储前一项的值,b始终存储当前项的值。

3.返回结果:函数最后返回b的值,即斐波那契数列的第n项。

这个例题展示了如何在C语言中使用滚动数组来计算斐波那契数列的第n项。通过这个例子,可以更好地理解滚动数组在解决动态规划问题中的应用,以及如何使用滚动数组来减少存储空间的使用。滚动数组通过重用数组空间来存储不同阶段的子问题解,从而避免了为每个阶段都创建一个新数组,提高了算法的空间效率。

常见线性DP问题

线性动态规划(Linear Dynamic Programming)问题是指那些状态转移只依赖于前一个或几个状态的动态规划问题。这类问题的特点是状态转移方程通常只涉及一维或二维的状态数组,因此它们的解决方案通常比多维状态的动态规划问题更简单、更直观。

特点:

1.状态转移简单:状态转移方程通常只涉及一维或二维的状态数组,使得状态转移过程简单明了。

2.空间复杂度低:由于状态转移的简单性,这类问题的空间复杂度通常较低,有时甚至可以优化到O(1)。

3.易于实现:线性动态规划问题的实现通常比较简单,容易编写和调试。

4.适用范围广:许多常见的动态规划问题都可以归类为线性动态规划问题,如最长公共子序列、最长上升子序列、最大子数组和等。

常见用法:

1.最长公共子序列:如经典的编辑距离问题。

2.最长上升子序列:如股票买卖问题。

3.最大子数组和:如最大子数组问题。

4.背包问题:如0/1背包问题和完全背包问题。

经典C语言例题:

题目: 使用线性动态规划解决最长上升子序列问题。

示例代码:

#include <stdio.h>
#include <stdlib.h>// 计算最长上升子序列的长度
int lengthOfLIS(int* nums, int numsSize) {int* dp = (int*)malloc(numsSize * sizeof(int));int maxLen = 0;for (int i = 0; i < numsSize; i++) {dp[i] = 1;for (int j = 0; j < i; j++) {if (nums[i] > nums[j] && dp[i] < dp[j] + 1) {dp[i] = dp[j] + 1;}}maxLen = (dp[i] > maxLen) ? dp[i] : maxLen;}int result = maxLen;free(dp);return result;
}// 计算最大值
int max(int a, int b) {return (a > b) ? a : b;
}int main() {int nums[] = {10, 9, 2, 5, 3, 7, 101, 18};int numsSize = sizeof(nums) / sizeof(nums[0]);printf("Length of longest increasing subsequence is: %d\n", lengthOfLIS(nums, numsSize));return 0;
}

例题分析:

1.计算最长上升子序列的长度lengthOfLIS函数接受一个整数数组nums和数组的大小numsSize作为参数,并计算最长上升子序列的长度。

2.初始化dp数组:函数首先创建一个与输入数组大小相同的dp数组,并初始化所有元素为1。

3.状态转移:函数通过两层循环遍历数组中的每个元素。对于每个元素nums[i],函数通过内层循环检查所有小于nums[i]的元素nums[j],并更新dp[i]的值,使其等于dp[j] + 1的最大值,前提是nums[i] > nums[j]

4.更新最大长度:在每次更新dp[i]后,函数检查dp[i]是否大于当前已知的最大长度maxLen,如果是,则更新maxLen

5.返回结果:函数最后返回maxLen的值,即最长上升子序列的长度。

这个例题展示了如何在C语言中使用线性动态规划解决最长上升子序列问题。通过这个例子,可以更好地理解线性动态规划在解决特定类型问题中的应用,以及如何使用线性动态规划来高效地解决问题。线性动态规划通过状态转移方程来计算问题的解,通常具有较低的空间复杂度,使得问题的解决方案更加高效和简洁。

背包问题

背包问题(Knapsack Problem)是计算机科学中的一个经典问题,属于组合优化问题。它描述的是这样一个场景:有一个背包,背包的承重有限,同时有一系列物品,每个物品都有自己的重量和价值。问题的目标是选择一些物品,使得这些物品的总重量不超过背包的承重,同时这些物品的总价值尽可能高。

特点:

1.组合优化:背包问题属于组合优化问题,它要求在有限的条件下选择最优的组合。

2.决策过程:问题的解决过程涉及到一系列的决策,即选择哪些物品放入背包。

3.重叠子问题:背包问题具有重叠子问题的特性,即在解决大问题的过程中会反复遇到相同的小问题。

4.最优子结构:背包问题具有最优子结构的特性,即问题的最优解包含其子问题的最优解。

常见用法:

1.资源分配:在资源有限的情况下,如何分配资源以最大化效益。

2.装载问题:如何装载货物以最大化运输效率。

3.时间管理:如何安排任务以最大化完成的工作量。

4.金融投资:如何分配投资以最大化收益。

经典C语言例题:

题目: 使用动态规划解决0/1背包问题。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
// 定义背包问题的结构体
typedef struct {int capacity; // 背包容量int* weights; // 物品重量数组int* values; // 物品价值数组int n; // 物品种类数
} Knapsack;// 创建背包问题实例
Knapsack* createKnapsack(int capacity, int* weights, int* values, int n) {Knapsack* knapsack = (Knapsack*)malloc(sizeof(Knapsack));knapsack->capacity = capacity;knapsack->weights = weights;knapsack->values = values;knapsack->n = n;return knapsack;
}// 计算最大值
int max(int a, int b) {return (a > b) ? a : b;
}// 计算最大价值
int knapsack_(Knapsack* knapsack) {int** dp = (int**)malloc((knapsack->n + 1) * sizeof(int*));for (int i = 0; i <= knapsack->n; i++) {dp[i] = (int*)malloc((knapsack->capacity + 1) * sizeof(int));memset(dp[i], 0, (knapsack->capacity + 1) * sizeof(int));}for (int i = 1; i <= knapsack->n; i++) {for (int w = 1; w <= knapsack->capacity; w++) {if (knapsack->weights[i - 1] <= w) {dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - knapsack->weights[i - 1]] + knapsack->values[i - 1]);} else {dp[i][w] = dp[i - 1][w];}}}int maxValue = dp[knapsack->n][knapsack->capacity];for (int i = 0; i <= knapsack->n; i++) {free(dp[i]);}free(dp);return maxValue;
}int main() {int capacity = 50;int weights[] = {10, 20, 30};int values[] = {60, 100, 120};int n = sizeof(weights) / sizeof(weights[0]);Knapsack* knapsack = createKnapsack(capacity, weights, values, n);printf("Maximum value in knapsack: %d\n", knapsack_(knapsack));free(knapsack->weights);free(knapsack->values);free(knapsack);return 0;
}

例题分析:

1.创建背包问题实例createKnapsack函数创建一个背包问题实例,包括背包容量、物品重量数组、物品价值数组和物品种类数。

2.计算最大价值knapsack函数使用动态规划方法计算背包问题的最大价值。它首先创建一个二维数组dp来存储子问题的解,然后通过两层循环遍历所有物品和所有可能的重量,计算每个子问题的解,并更新dp数组。

3.主函数:在main函数中,定义了一个背包问题实例,并调用knapsack函数计算最大价值,最后打印结果。

这个例题展示了如何在C语言中使用动态规划解决0/1背包问题。通过这个例子,可以更好地理解动态规划在解决背包问题中的应用,以及如何使用动态规划来高效地解决具有重叠子问题和最优子结构性质的问题。动态规划通过存储子问题的解来避免重复计算,从而提高了算法的效率。

最长公共子序列(LCS)

最长公共子序列(Longest Common Subsequence,LCS)问题是计算机科学中的一个经典问题,属于动态规划领域。它描述的是这样一个场景:有两个序列X和Y,找出一个最长的子序列,这个子序列在X和Y中都出现过,且在X和Y中的相对顺序与子序列中的相对顺序相同。

特点:

1.子序列:LCS问题寻找的是两个序列的子序列,而不是子串。子序列不要求连续,而子串要求连续。

2.相对顺序:子序列中的元素在原序列中的相对顺序必须保持不变。

3.最优子结构:LCS问题具有最优子结构的特性,即问题的最优解包含其子问题的最优解。

4.重叠子问题:LCS问题具有重叠子问题的特性,即在解决大问题的过程中会反复遇到相同的小问题。

常见用法:

1.生物信息学:在生物信息学中,LCS可以用于比较DNA序列或蛋白质序列。

2.版本控制系统:在版本控制系统中,LCS可以用来比较不同版本的文件。

3.文本编辑:在文本编辑器中,LCS可以用来实现自动完成和代码补全功能。

4.数据压缩:在数据压缩中,LCS可以用来找出重复的子序列,从而实现压缩。

经典C语言例题:

题目: 使用动态规划解决最长公共子序列问题。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
// 计算最大值
int max(int a, int b) {return (a > b) ? a : b;
}
// 计算最长公共子序列的长度
int longestCommonSubsequence(char* text1, char* text2) {int len1 = strlen(text1);int len2 = strlen(text2);int** dp = (int**)malloc((len1 + 1) * sizeof(int*));for (int i = 0; i <= len1; i++) {dp[i] = (int*)malloc((len2 + 1) * sizeof(int));memset(dp[i], 0, (len2 + 1) * sizeof(int));}for (int i = 1; i <= len1; i++) {for (int j = 1; j <= len2; j++) {if (text1[i - 1] == text2[j - 1]) {dp[i][j] = dp[i - 1][j - 1] + 1;} else {dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);}}}int result = dp[len1][len2];for (int i = 0; i <= len1; i++) {free(dp[i]);}free(dp);return result;
}int main() {char text1[] = "AGGTAB";char text2[] = "GXTXAYB";printf("Length of LCS is: %d\n", longestCommonSubsequence(text1, text2));return 0;
}

例题分析:

1.初始化dp数组longestCommonSubsequence函数首先创建一个二维数组dp,其大小为两个字符串长度加1,用于存储子问题的解。

2.状态转移:函数通过两层循环遍历两个字符串的每个字符。对于每个字符对text1[i - 1]text2[j - 1],如果它们相等,则dp[i][j]的值为dp[i - 1][j - 1] + 1;如果不相等,则dp[i][j]的值为max(dp[i - 1][j], dp[i][j - 1])

3.返回结果:函数最后返回dp[len1][len2]的值,即两个字符串的最长公共子序列的长度。

这个例题展示了如何在C语言中使用动态规划解决最长公共子序列问题。通过这个例子,可以更好地理解动态规划在解决LCS问题中的应用,以及如何使用动态规划来高效地解决问题。动态规划通过状态转移方程来计算问题的解,通常具有较低的空间复杂度,使得问题的解决方案更加高效和简洁。

最长递增子序列(LIS)

最长递增子序列(Longest Increasing Subsequence,LIS)问题是计算机科学中的一个经典问题,属于动态规划领域。它描述的是这样一个场景:给定一个序列,找出一个最长的子序列,这个子序列中的元素是严格递增的。

特点:

1.递增子序列:LIS问题寻找的是一个递增的子序列,即子序列中的每个元素都比前一个元素大。

2.最优子结构:LIS问题具有最优子结构的特性,即问题的最优解包含其子问题的最优解。

3.重叠子问题:LIS问题具有重叠子问题的特性,即在解决大问题的过程中会反复遇到相同的小问题。

4.动态规划解法:LIS问题通常使用动态规划来解决,通过维护一个数组来存储每个位置的最长递增子序列的长度。

常见用法:

1.生物信息学:在生物信息学中,LIS可以用于分析DNA序列或蛋白质序列中的递增模式。

2.金融分析:在金融分析中,LIS可以用于识别股票价格的递增趋势。

3.数据压缩:在数据压缩中,LIS可以用于找出重复的递增模式,从而实现压缩。

4.路径规划:在路径规划中,LIS可以用于找出最短的递增路径。

经典C语言例题:

题目: 使用动态规划解决最长递增子序列问题。

示例代码:

#include <stdio.h>
#include <stdlib.h>// 计算最长递增子序列的长度
int lengthOfLIS(int* nums, int numsSize) {int* dp = (int*)malloc(numsSize * sizeof(int));int maxLen = 0;for (int i = 0; i < numsSize; i++) {dp[i] = 1;for (int j = 0; j < i; j++) {if (nums[i] > nums[j] && dp[i] < dp[j] + 1) {dp[i] = dp[j] + 1;}}maxLen = (dp[i] > maxLen) ? dp[i] : maxLen;}int result = maxLen;free(dp);return result;
}// 计算最大值
int max(int a, int b) {return (a > b) ? a : b;
}int main() {int nums[] = {10, 9, 2, 5, 3, 7, 101, 18};int numsSize = sizeof(nums) / sizeof(nums[0]);printf("Length of longest increasing subsequence is: %d\n", lengthOfLIS(nums, numsSize));return 0;
}

例题分析:

1.初始化dp数组lengthOfLIS函数首先创建一个与输入数组大小相同的dp数组,并初始化所有元素为1。

2.状态转移:函数通过两层循环遍历数组中的每个元素。对于每个元素nums[i],函数通过内层循环检查所有小于nums[i]的元素nums[j],并更新dp[i]的值,使其等于dp[j] + 1的最大值,前提是nums[i] > nums[j]

3.更新最大长度:在每次更新dp[i]后,函数检查dp[i]是否大于当前已知的最大长度maxLen,如果是,则更新maxLen

4.返回结果:函数最后返回maxLen的值,即最长递增子序列的长度。

这个例题展示了如何在C语言中使用动态规划解决最长递增子序列问题。通过这个例子,可以更好地理解动态规划在解决LIS问题中的应用,以及如何使用动态规划来高效地解决问题。动态规划通过状态转移方程来计算问题的解,通常具有较低的空间复杂度,使得问题的解决方案更加高效和简洁。

状态压缩DP

状态压缩动态规划(State Compression Dynamic Programming)是一种动态规划的优化技术,它用于减少动态规划中状态表示的空间复杂度。在某些动态规划问题中,状态的表示可以非常紧凑,通过使用位运算或其他技巧来压缩状态空间,从而减少所需的存储空间。

特点:

1.空间优化:状态压缩动态规划通过压缩状态空间来减少存储空间的使用,通常可以将空间复杂度从多项式级别降低到对数级别。

2.位运算:状态压缩通常涉及位运算,如按位与(&)、按位或(|)、按位异或(^)和左移(<<)、右移(>>)等。

3.易于实现:状态压缩的实现通常比较简单,只需要对状态进行适当的编码和解码。

4.适用范围:状态压缩动态规划适用于那些状态可以被压缩的问题,如子集和问题、硬币找零问题等。

常见用法:

1.子集和问题:如0/1背包问题,其中每个物品可以选择放入或不放入背包。

2.硬币找零问题:如给定面额的硬币,求最少硬币数来组成特定金额。

3.图着色问题:如使用最少的颜色给图中的顶点着色,使得相邻的顶点颜色不同。

经典C语言例题:

题目: 使用状态压缩动态规划解决子集和问题。

示例代码:

#include <stdio.h>
#include <stdlib.h>// 计算子集和问题的解
int subsetSum(int* nums, int numsSize, int sum) {int dp[1 << numsSize];memset(dp, 0, sizeof(dp));dp[0] = 1;for (int i = 0; i < (1 << numsSize); i++) {for (int j = 0; j < numsSize; j++) {if (i & (1 << j)) {dp[i] |= dp[i ^ (1 << j)];}}}return dp[(1 << numsSize) - 1] & (1 << sum);
}int main() {int nums[] = {1, 2, 3};int numsSize = sizeof(nums) / sizeof(nums[0]);int sum = 4;printf("Subset sum %d is %s\n", sum, subsetSum(nums, numsSize, sum) ? "possible" : "not possible");return 0;
}

例题分析:

1.初始化dp数组subsetSum函数首先创建一个大小为1 << numsSizedp数组,用于存储所有可能的子集的和。

2.状态转移:函数通过两层循环遍历所有子集。外层循环遍历所有可能的子集,内层循环检查当前子集中是否包含第j个元素。

3.状态压缩:在内层循环中,如果当前子集包含第j个元素,则使用按位异或运算符^来生成不包含第j个元素的子集,并将这两个子集的和进行按位或运算,以更新dp数组。

4.返回结果:函数最后检查dp[(1 << numsSize) - 1]是否包含sum,如果是,则返回1(表示可能),否则返回0(表示不可能)。

这个例题展示了如何在C语言中使用状态压缩动态规划解决子集和问题。通过这个例子,可以更好地理解状态压缩动态规划在解决特定类型问题中的应用,以及如何使用状态压缩技术来高效地解决问题。状态压缩动态规划通过压缩状态空间来减少存储空间的使用,使得问题的解决方案更加高效和简洁。

树形DP

树形动态规划(Tree Dynamic Programming)是一种动态规划的变体,它在树形结构上进行状态的定义和转移。树形动态规划通常用于解决树形结构上的优化问题,如树形背包问题、树形路径问题等。

特点:

1.树形结构:树形动态规划适用于树形结构的数据,如树形图、树形网络等。

2.状态定义:在树形动态规划中,状态的定义通常与树的节点相关,每个节点可以有一个或多个状态。

3.状态转移:状态转移依赖于树的结构,通常需要考虑父节点和子节点之间的关系。

4.递归实现:树形动态规划通常通过递归函数来实现,每个节点的状态转移依赖于其子节点的状态。

常见用法:

1.树形背包问题:在树形结构上进行背包问题的求解。

2.树形路径问题:在树形结构上求解最长路径、最小路径等问题。

3.树形决策问题:在树形结构上进行决策问题的求解,如树形博弈问题。

经典C语言例题:

题目: 使用树形动态规划解决树形背包问题。

示例代码:

#include <stdio.h>
#include <stdlib.h>// 定义树形背包问题的结构体
typedef struct TreeNode {int weight;int value;struct TreeNode* left;struct TreeNode* right;
} TreeNode;// 创建树形背包问题的节点
TreeNode* createTreeNode(int weight, int value) {TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));node->weight = weight;node->value = value;node->left = NULL;node->right = NULL;return node;
}// 计算树形背包问题的最大价值
int treeKnapsack(TreeNode* root, int capacity) {if (root == NULL) {return 0;}// 如果当前节点的重量大于背包容量,则不能选择当前节点if (root->weight > capacity) {return treeKnapsack(root->left, capacity) + treeKnapsack(root->right, capacity);} else {// 选择当前节点,计算剩余容量下的最大价值int include = root->value + treeKnapsack(root->left, capacity - root->weight) + treeKnapsack(root->right, capacity - root->weight);// 不选择当前节点,只计算左右子树的最大价值int exclude = treeKnapsack(root->left, capacity) + treeKnapsack(root->right, capacity);// 返回两者中的最大值return (include > exclude) ? include : exclude;}
}int main() {TreeNode* root = createTreeNode(10, 100);root->left = createTreeNode(20, 150);root->right = createTreeNode(30, 200);int capacity = 50;printf("Maximum value in knapsack: %d\n", treeKnapsack(root, capacity));free(root->left);free(root->right);free(root);return 0;
}

例题分析:

1.创建树形背包问题的节点createTreeNode函数创建树形背包问题的节点,并初始化节点的重量和价值。

2.计算树形背包问题的最大价值treeKnapsack函数使用递归方法计算树形背包问题的最大价值。函数首先检查当前节点是否为空,如果为空,则返回0。如果当前节点的重量大于背包容量,则不能选择当前节点,函数返回左右子树的最大价值之和。如果当前节点的重量小于或等于背包容量,则可以选择当前节点,函数计算包括当前节点在内的剩余容量下的最大价值,并与不选择当前节点的情况进行比较,返回两者中的最大值。

3.主函数:在main函数中,创建了一个树形背包问题的实例,并调用treeKnapsack函数计算最大价值,最后打印结果。

这个例题展示了如何在C语言中使用树形动态规划解决树形背包问题。通过这个例子,可以更好地理解树形动态规划在解决树形结构上的优化问题中的应用,以及如何使用递归方法来高效地解决问题。树形动态规划通过在树形结构上定义和转移状态,使得问题的解决方案更加高效和简洁。

数位DP

数位动态规划(Digit DP)是一种特殊的动态规划技术,它用于解决与数字相关的计数问题。数位动态规划通常用于计算在一定范围内满足特定条件的数字的数量,例如计算在一定范围内有多少个数字是回文数、有多少个数字满足特定的数位和条件等。

特点:

1.数位处理:数位动态规划涉及对数字的每一位进行单独处理,通常需要将数字转换为数位数组的形式。

2.状态定义:数位动态规划的状态通常与数字的数位有关,每个状态可能代表一个或多个数位的组合。

3.状态转移:状态转移依赖于数位之间的关系,如进位、借位等。

4.记忆化搜索:数位动态规划通常使用记忆化搜索技术来避免重复计算,提高效率。

常见用法:

1.计算回文数数量:在一定范围内计算有多少个回文数。

2.计算满足数位和的数字数量:在一定范围内计算有多少个数字的数位和满足特定条件。

3.计算满足特定数位模式的数字数量:在一定范围内计算有多少个数字符合特定的数位模式。

经典C语言例题:

题目: 使用数位动态规划计算在一定范围内有多少个回文数。

示例代码:

#include <stdio.h>
#include <string.h>// 计算回文数的数量
int countPalindromes(int L, int R) {int dp[10][10][10]; // dp[i][j][k]表示长度为i,最高位为j,最高位前一位为k的回文数数量memset(dp, 0, sizeof(dp));for (int i = 0; i < 10; i++) {dp[1][i][i] = 1; // 单位数的回文数数量}for (int len = 2; len <= R; len++) {for (int j = 0; j < 10; j++) {for (int k = 0; k < 10; k++) {for (int m = 0; m < 10; m++) {if (j == m) {dp[len][j][k] += dp[len - 1][k][m];} else {dp[len][j][k] += dp[len - 1][k][m] / 2;}}}}}int result = 0;for (int j = 0; j < 10; j++) {for (int k = 0; k < 10; k++) {result += dp[R - L + 1][j][k];}}return result;
}int main() {int L = 100, R = 999;printf("Number of palindromes between %d and %d is: %d\n", L, R, countPalindromes(L, R));return 0;
}

例题分析:

1.初始化dp数组countPalindromes函数首先创建一个三维数组dp,用于存储不同长度、最高位和最高位前一位的回文数数量。

2.状态转移:函数通过三层循环遍历所有可能的回文数长度、最高位和最高位前一位。对于每个状态,函数计算所有可能的前一位和当前位组合的数量,并更新dp数组。

3.计算结果:函数最后遍历所有可能的最高位和最高位前一位,将它们与长度R - L + 1组合,计算出在指定范围内的回文数数量,并返回结果。

这个例题展示了如何在C语言中使用数位动态规划计算在一定范围内有多少个回文数。通过这个例子,可以更好地理解数位动态规划在解决与数字相关的计数问题中的应用,以及如何使用数位动态规划来高效地解决问题。数位动态规划通过在数位层面上定义和转移状态,使得问题的解决方案更加高效和简洁。

相关文章:

竞赛常考的知识点大总结(五)动态规划

DP问题的性质 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是指在解决动态规划问题时所依赖的一些基本特征和规律。动态规划是一种将复杂问题分解为更小子问题来解决的方法&#xff0c;它适用于具有重叠子问题和最优子结构性质的问题。动态规划问题通常…...

如何在 Mac 上恢复已删除的数据

如果您丢失了 Mac 上的数据&#xff0c;请不要绝望。恢复数据比您想象的要容易&#xff0c;并且有很多方法可以尝试。 在 Mac 上遭受数据丢失是每个人都认为永远不会发生在他们身上的事情之一......直到它发生。不过&#xff0c;请不要担心&#xff0c;因为您可以通过多种方法…...

Java笔试题总结

HashSet子类依靠()方法区分重复元素。 A toString(),equals() B clone(),equals() C hashCode(),equals() D getClass(),clone() 答案:C 解析: 先调用对象的hashcode方法将对象映射为数组下标,再通过equals来判断元素内容是否相同 以下程序执行的结果是&#xff1a; class X{…...

github本地仓库push到远程仓库

1.从远程仓库clone到本地 2.生成SSH秘钥&#xff0c;为push做准备 在Ubuntu命令行输入一下内容 [rootlocalhost ~]# ssh-keygen -t rsa < 建立密钥对&#xff0c;-t代表类型&#xff0c;有RSA和DSA两种 Generating public/private rsa key pair. Enter file in whi…...

Error: TF_DENORMALIZED_QUATERNION: Ignoring transform forchild_frame_id

问题 运行程序出现&#xff1a; Error: TF_DENORMALIZED_QUATERNION: Ignoring transform for child_frame_id “odom” from authority “unknown_publisher” because of an invalid quaternion in the transform (0.0 0.0 0.0 0.707) 主要是四元数没有归一化 Eigen::Quatern…...

Linux从入门到精通 --- 2.基本命令入门

文章目录 第二章&#xff1a;2.1 Linux的目录结构2.1.1 路径描述方式 2.2 Linux命令入门2.2.1 Linux命令基础格式2.2.2 ls命令2.2.3 ls命令的参数和选项2.2.4 ls命令选项的组合使用 2.3 目录切换相关命令2.3.1 cd切换工作目录2.3.2 pwd查看当前工作目录2.4 相对路径、绝对路径和…...

Redis常用命令补充和持久化

一、redis 多数据库常用命令 1.1 多数据库间切换 1.2 多数据库间移动数据 1.3 清除数据库内数据 1.4 设置密码 1.4.1 使用config set requirepass yourpassword命令设置密码 1.4.2 使用config get requirepass命令查看密码 二、redis高可用 2.1 redis 持久化 2.1.1 持…...

【记录】海康相机(SDK)二次开发时的错误码

海康相机&#xff08;SDK&#xff09;二次开发时的错误码 在进行海康sdk二次开发的时候&#xff0c;经常碰到各种错误&#xff0c;遂结合官方文档和广大网友的一些经验&#xff0c;把这些错误码记录一下&#xff0c;方便查找。笔者使用的SDK版本是HCNetSDKV6.1.9.4。 错误类型…...

端盒日记Day02

JS 本本本本本地存储 localStorage 作用&#xff1a;可以将数据永久存储在本地&#xff08;用户电脑&#xff09;&#xff0c;除非手动删除&#xff0c;否则关闭页面也会存在 特性&#xff1a;a.可多窗口&#xff08;页面&#xff09;共享&#xff08;同一浏览器可以共享&a…...

考研高数(平面图形的面积,旋转体的体积)

1.平面图形的面积 纠正&#xff1a;参数方程求面积 2.旋转体的体积&#xff08;做题时&#xff0c;若以x为自变量不好计算&#xff0c;可以求反函数&#xff0c;y为自变量进行计算&#xff09;...

选择企业邮箱,扬帆迈向商务新纪元!

企业邮箱和个人邮箱不同&#xff0c;它的邮箱后缀是企业自己的域名。企业邮箱供应商一般都提供手机app、桌面端、web浏览器访问等邮箱使用途径。那么什么是企业邮箱&#xff1f;如何选择合适的企业邮箱&#xff1f;好用的企业邮箱应具备无缝迁移、协作、多邮箱管理等功能。 企…...

2024.3.25力扣每日一题——零钱兑换2

2024.3.25 题目来源我的题解方法一 动态规划 题目来源 力扣每日一题&#xff1b;题序&#xff1a;518 我的题解 方法一 动态规划 给定总金额 amount 和数组 coins&#xff0c;要求计算金额之和等于 amount 的硬币组合数。其中&#xff0c;coins的每个元素可以选取多次&#…...

包子凑数【蓝桥杯】/完全背包

包子凑数 完全背包 完全背包问题和01背包的区别就是&#xff0c;完全背包问题每一个物品能取无限次。 思路&#xff1a;当n个数的最大公约数不为1&#xff0c;即不互质时&#xff0c;有无限多个凑不出来的&#xff0c;即n个数都可以表示成kn&#xff0c;k为常数且不为1。当n个…...

口语 4.6

drop the gun :逃避 radically 极大程度地 vastly cognition&#xff1a;认知能力 flaw缺陷 flawless&#xff1a;没有缺陷 interface&#xff1a;接口&#xff0c;交流处 retain&#xff1a;保留 down the rabbit hole&#xff1a;进入未知领域了 wrap your head aro…...

使用Docker 部署jenkins 实现自动化部署

使用Docker部署jenkins实现自动化部署ruoyi-vue docker jenkinsJava jenkinsfilevue jenkinsfileDockerfile 部署脚本Java Dockerfilenginx Dockerfilenginx-dev.conf 使用docker部署Jenkins&#xff0c;项目: https://gitee.com/y_project/RuoYi-Vue 作为部署项目示范 docker…...

golang语言系列:Web框架+路由 之 Gin

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是golang语言学习系列&#xff0c;本篇对Gin框架的基本使用方法进行学习 1.Gin框架是什么 Gin 是一个 Go (Golang) 编写的轻量级 http web 框架&#xff0c;运行速度非常快&#xff0c;如果你是性能和高效的追求者…...

春招百题--堆

一、堆的定义 二、堆&#xff08;优先队列&#xff09; 堆通常用于实现优先队列&#xff08;priority_queue&#xff09;&#xff0c;大顶堆相当于元素按从大到小的顺序出队的优先队列。从使用角度来看&#xff0c;我们可以将“优先队列”和“堆”看作等价的数据结构。 堆的…...

全志A40i android7.1 移植wifi驱动的一般流程

一&#xff0c;问题分析 一般情况下移植一款模组&#xff0c;会涉及到驱动&#xff0c;firmware, hal层&#xff0c;方案端的适配。 下面以RTL8723ds为例详细列出移植的通用步骤。 二&#xff0c;移植步骤 1. 移植Wi-Fi驱动 从RTL原厂或者已经支持的其他把内核版本中获取驱动…...

Qt——Qt绘图之QPainter的使用总结(使用paintEvent实现旋转图片效果)

【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实战》...

Day83:服务攻防-开发组件安全JacksonFastJson各版本XStreamCVE环境复现

目录 J2EE-组件Jackson-本地demo&CVE 代码执行 (CVE-2020-8840) 代码执行 (CVE-2020-35728&#xff09; J2EE-组件FastJson-本地demo&CVE FastJson < 1.2.24 FastJson < 1.2.47 FastJson < 1.2.80 (利用条件比较苛刻) J2EE-组件XStream-靶场&CVE …...

【QT+QGIS跨平台编译】056:【pdal_kazhdan+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

点击查看专栏目录 文章目录 一、pdal_kazhdan介绍二、pdal下载三、文件分析四、pro文件五、编译实践一、pdal_kazhdan介绍 pdal_kazhdan 是 PDAL(Point Data Abstraction Library)相关的 Kazhdan 算法的实现。PDAL 是一个用于处理和分析点云数据的开源库,而 Kazhdan 算法通常…...

泰坦尼克号幸存者数据分析

泰坦尼克号幸存者数据分析 1、泰坦尼克号数据集2、数据集加载与概览3、泰坦尼克号幸存者数据分析4、哪些人可能成为幸存者&#xff1f; 1、泰坦尼克号数据集 泰坦尼克号的沉没是世界上最严重的海难事故之一&#xff0c;造成了大量的人员伤亡。这是一艘号称当时世界上最大的邮轮…...

Memcached 教程之 PHP 连接 Memcached 服务(十)

PHP 连接 Memcached 服务 在前面章节中我们已经介绍了如何安装 Memcached 服务&#xff0c;接下来我们为大家介绍 PHP 如何使用 Memcached 服务。 PHP Memcache 扩展安装 PHP Memcache 扩展包下载地址&#xff1a;PECL :: Package :: memcache&#xff0c;你可以下载最新稳定…...

【zlm】音视频流与音频流合并的设计

目录 设想一 设想二 方案三 关键技术 测试语句 测试脚本 参考文档 设想一 //开始录制_option.mp4_save_path custom_path;_option.mp4_max_second max_second;vector<Track::Ptr> mytracks getTracks();auto src MediaSource::find( DEFAULT_VHOST, "1&quo…...

typescript的工作流

先coding code.ts代码&#xff0c;由tsc编译code.ts生成code.js格式 npm install —save-dev lite-server 是用来安装轻量级的服务器&#xff0c;只是用来开发的一个服务器&#xff0c;真正到生产环境中时可能会使用类似于Apache的server或者汤姆猫一类的服务器&#xff0c;安…...

MATLAB下载与安装详细教程:从官方获取到成功启动

引言 MATLAB&#xff08;MATrix LABoratory&#xff09;作为一款全球知名的高级数值计算与数据分析平台&#xff0c;以其强大的矩阵运算能力、丰富的内置函数库以及直观易用的图形用户界面&#xff0c;深受科研人员、工程师和学生群体的青睐。无论是进行复杂的数学建模、信号处…...

【随笔】Git 高级篇 -- 分离 HEAD(十一)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…...

mac、windows 电脑安装使用多个版本的node

我们为啥要安装多个不同版本的node&#xff1f; 开发旧项目时&#xff0c;使用低版本Nodejs。开发新项目时&#xff0c;需使用高版本Node.js。可使用n同时安装多个版本Node.js&#xff0c;并切换到指定版本Node.js。 mac电脑安装 一、全局安装 npm install -g n 二、mac电脑…...

vue 浅解watch cli computed props ref vue slot axios nexttick devtools说明使用

Vue.js 是一个强大的前端框架&#xff0c;它提供了很多有用的功能和工具。你提到的这些特性&#xff08;watch、cli、computed、props、ref、slot、axios、nextTick、devtools&#xff09;在 Vue 中各自扮演着不同的角色。下面我会逐一解释这些特性如何在 Vue 中使用&#xff1…...

Unity自定义框架(1)-----------单例模式

前言&#xff1a; Unity作为一款强大的游戏开发引擎&#xff0c;其基础框架的设计对于项目的结构和性能有着重要的影响。其中&#xff0c;单例模式是一种常用的设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点。 什么是单例模式&#xff1f…...

做博彩类的网站/金华seo全网营销

中文显示是乱码&#xff0c;更改为英文&#xff0c;方法&#xff1a;修改/etc/sysconfig/i18n,其中有LANG和LANGUAGE开头的两行&#xff0c;以前是ZH_CN.GB18030&#xff0c;改成en_US就可以了。具体改成什么可以在下面的SUPPORTED那一行里面选择。这个方法不一定标准&#xff…...

做网站的收入/今日新闻快报

收集到了一份深入浅出springboot的学习文档&#xff1a;讲主要从这个几个方面带领大家一起来学习由于内容太多&#xff0c;我就不一一截取出来了目录里面还有很多细化的部分&#xff0c;有需要的可以关注我之后私信回复【文档】来免费获取到这一份springboot 学习文档&#xff…...

建设厅网站沙场限期通知书/seo品牌优化百度资源网站推广关键词排名

今天探讨了下关于python的一个练习剪刀石头布&#xff0c;可能程序还是多少有些不足&#xff0c;欢迎各位批评指正&#xff1a; 代码如下&#xff1a; import random def main(): #将玩家和机器视为两个参量&#xff0c;机器产生随机数1-3&#xff0c;代表着1:石头,2:剪刀,3:布…...

怎么用网站视频做自媒体/怎么网上宣传自己的产品

中央各大会议20天4次部署“新基建”&#xff01;首次重点提及数据中心&#xff0c;两提5G。会议指出&#xff0c;要加快5G网络、数据中心等新型基础设施建设进度&#xff0c;并注重调动民间投资积极性&#xff0c;5年3.5万亿元投资在路上&#xff0c;行业前景广阔。 &#xff0…...

通化网站制作/平台软件定制开发

Mac跟新完WireShark以后&#xff0c;发现启动WireShark抓包爆如下错 Wireshark: The capture session could not be initiated on 查资料&#xff0c;发现是没有权限查看/dev/bfp*这些文件夹造成的。于是修改文件夹解决了问题。 sudo chmod or /dev/bpf*...

做网站接活犯法吗/收录优美图片

您的问题的最佳解决方案是使用池.使用队列并具有单独的“队列馈送”功能可能有点过分.这是一个稍微重新安排的程序版本,这次只有2个进程在池中.我认为这是最简单的方法,对原始代码的改动很小&#xff1a;import multiprocessingimport timedata ([a, 2], [b, 4], [c, 6], [d, …...