目录
参考链接:https://www.cnblogs.com/DarrenChan/p/8734203.html
参考链接:https://blog.csdn.net/weixin_40288381/article/details/89304276
1. 斐波那契数列
斐波那契数列:1,1,2,3,5,8......
状态转移方程:F(N) = F(N - 1) + F(N - 2)
int fb(int n){
if (n < 1){
return 0;
}
if (n == 1 || n == 2){
return 1;
}
int first = 1;
int second = 1;
int third = 2;
for (int i = 3; i <= n; ++i){
third = first + second;
first = second;
second = third;
}
return third;
}
2. 台阶问题
有n级台阶,一个人每次上一级或者两级,问有多少种走完N级台阶的方法。为了防止溢出,请将结果Mod 1000000007。
状态转移方程:F(N) = F(N - 1) + F(N - 2)
int step(int n){
if (n < 1){
return 0;
}
if (n == 1){
return 1;
}
if (n == 2){
return 2;
}
int first = 1;
int second = 2;
int third = 3;
for (int i = 3; i <= n; ++i){
third = (first + second)%1000000007;
first = second;
second = third;
}
return third;
}
3. 母牛生牛问题
母牛每年生一只母牛,新出生的母牛成长三年后也能每年生一只母牛,假设不会死。求N年后,母牛的数量。
状态转移方程:F(N) = F(N - 1) + F(N - 3)
int reproduce(int n){
if (n < 1){
return 0;
}
if (n==1 || n==2 || n==3){
return n;
}
int first = 1;
int second = 2;
int third = 3;
int fourth = 4;
for (int i = 4; i <= n; ++i){
fourth = third + first;
first = second;
second = third;
third = fourth;
}
return fourth;
}
4. 找零钱问题
有数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim(小于等于1000)代表要找的钱数,求换钱有多少种方法。
测试样例:[1,2,4],3
返回:2
状态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-price[i]]
int change(vector<int> price, int sum){
int n = price.size();
vector<vector<int>>dp(n, vector<int>(sum+1));
for (int i = 0; i < n; ++i){
dp[i][0] = 1;
}
for (int i = 0; i*price[0] <= sum; ++i){
dp[0][i*price[0]] = 1;
}
for (int i = 1; i < n; ++i){
for (int j = 1; j <= sum; ++j){
dp[i][j] = dp[i - 1][j] + (j>price[i] ? dp[i][j - price[i]] : 0);
}
}
return dp[n-1][sum];
}
优化:dp[j] = dp[j] + dp[j-price[i]]
int change2(vector<int> price, int sum){
int n = price.size();
vector<int>dp(sum + 1);
dp[0] = 1;
for (int i = 0; i < n; ++i){
for (int j = price[i]; j <= sum; ++j){
dp[j] = dp[j] + dp[j - price[i]];
}
}
return dp[sum];
}
5. 矩阵最小路径
基础题不做介绍
6. 最长递增子序列
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4
状态转移方程:dp[i] = max{dp[i],dp[j]+1(nums[i]>nums[j])}(j<i)
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if(n == 0)
return 0;
int dp[n];
int Max = 0;
for(int i=0; i<n; ++i){
int s = 0;
for(int j=0; j<i; ++j){
if(nums[i]>nums[j] && dp[j]>s){
s = dp[j];
}
}
dp[i] = s+1;
if(Max < dp[i])
Max = dp[i];
}
return Max;
}
记录递增中最长子序列,时间复杂度为nlog(n).
思路:遇到小的,直接替换掉第一个比它大的数
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if (n == 0) return 0;
vector<int> ret;
for (int i = 0; i < n; ++i) {
if (ret.empty() || nums[i] > ret.back()) {
ret.push_back(nums[i]);
} else {
*lower_bound(ret.begin(), ret.end(), nums[i]) = nums[i];
}
}
return ret.size();
}
7. 最长公共子序列
给定两个字符串A和B,返回两个字符串的最长公共子序列的长度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最长公共子序列。
状态转移方程:A[i] == B[j] dp[i][j] = max{dp[i][j], dp[i-1][j-1]+1}
A[i] != B[j] dp[i][j] = max{dp[i-1][j], dp[i-1][j-1]} (dp[i][j]表示i,j范围内的连续子序列长度)
int getLCS(string A, string B){
int n = A.size();
int m = B.size();
vector<vector<int>> dp(n, vector<int>(m, 0));
dp[0][0] = A[0] == B[0] ? 1 : 0;
for (int i = 1; i < n; ++i){
dp[i][0] = fmax(dp[i - 1][0], A[i] == B[0] ? 1 : 0);
}
for (int j = 1; j < m; ++j){
dp[0][j] = fmax(dp[0][j - 1], A[0] == B[j] ? 1 : 0);
}
for (int i = 1; i < n; ++i){
for (int j = 1; j < m; ++j){
if (A[i] == B[j]){
dp[i][j] = fmax(dp[i][j],dp[i-1][j-1]+1);
}
else{
dp[i][j] = fmax(dp[i-1][j], dp[i][j - 1]);
}
}
}
return dp[n-1][m-1];
}
8. 最长公共子串
A="1A2C3D4B56A”,B="B12C3D4B56A”,”2C3D4B56A"是最长公共子串。
状态转移方程:dp[i][j] = dp[i-1][j-1]+1 (dp[i][j]表示以i,j结尾的连续子串长度)
int getLCSC(string A, string B){
int n = A.size();
int m = B.size();
vector<vector<int>> dp(n, vector<int>(m, 0));
dp[0][0] = A[0] == B[0] ? 1 : 0;
for (int i = 1; i < n; ++i){
dp[i][0] = A[i] == B[0] ? 1 : 0;
}
for (int j = 1; j < m; ++j){
dp[0][j] = A[0] == B[j] ? 1 : 0;
}
for (int i = 1; i < n; ++i){
for (int j = 1; j < m; ++j){
if (A[i] == B[j]){
dp[i][j] = dp[i - 1][j - 1] + 1;
}
}
}
int MAX = 0;
for (int i = 1; i < n; ++i){
for (int j = 1; j < m; ++j){
if (MAX<dp[i][j]){
MAX = dp[i][j];
}
}
}
return MAX;
}
9. 最长回文子序列
给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000。
示例
输入:"bbbab" 输出:4
状态转移方程:s[i] == s[j] f[i][j] = f[i+1][j-1] + 2;
else f[i][j] = max(f[i+1][j] , f[i][j-1]);
int longestPalindromeSubseq(string s) {
int n=s.size();
vector<vector<int>>f(n,vector<int>(n,0));
for(int i=0; i<n; i++)f[i][i]=1;
for(int len=2; len<=n; len++)
for(int i=0; i<n-len+1; i++){
int j=i+len-1;
if(s[i]==s[j])
f[i][j]=f[i+1][j-1]+2;
else
f[i][j]=max(f[i+1][j],f[i][j-1]);
}
return f[0][n-1];
}
10. 背包问题
参考链接:https://blog.csdn.net/tinyguyyy/article/details/51203935
01背包:dp[j]=max(dp[j],dp[j-weight[i]]+value[i]); j从大到小
完全背包:dp[j]=max(dp[j],dp[j-weight[i]]+value[i]); j从小到大
多重背包:如果数量*体积>总容量,转为完全背包,其他变为01背包(数量可以优化为1,2,4,A,...M-A作为权重)
要求恰好装满背包,那么在初始化时除了F [0]为0,其它F [1::V ]均设为-1,这样就可以保证最终得到的F [V ]是一种恰好装满背包的
最优解。如果没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将F [0::V ]全部设为0。
11. 最长整除子序列
给出一个由无重复的正整数组成的集合,找出其中最大的整除子集,子集中任意一对 (Si,Sj) 都要满足:Si % Sj = 0 或 Sj % Si = 0。
如果有多个目标子集,返回其中任何一个均可。(同最大上升子序列)
vector<int> largestDivisibleSubset(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(),nums.end());
vector<int> dp(n);
vector<int> out;
for(int i=1; i<n; ++i){
for(int j=0; j<i; ++j){
if(nums[i]%nums[j] == 0){
dp[i] = max(dp[i], dp[j]+1);
}
}
}
int MAX = -1;
int idx = -1;
for(int i=0; i<n; ++i){
if(MAX < dp[i]){
MAX = dp[i];
idx = i;
}
}
for(int i=idx; i>=0; --i){
if(dp[i] == MAX){
out.push_back(nums[i]);
--MAX;
}
}
reverse(out.begin(), out.end());
return out;
}
12. 寻找和为定值的多少组合
题目:输入两个整数n和sum,从数列1,2,3.......n 中随意取几个数,使其和等于sum,要求将其中所有的可能组合列出来
需要输出,可以设计标记位进行输出
递归:
int sum(int n, int m){
if (m <= 0 || n <= 0)
return 0;
return sum(n - 1, m) + sum(n - 1, m - n) + (m == n ? 1 : 0);
}
动态规划:
int sum_dp(int n, int m){
vector<vector<int>> dp(n+1, vector<int>(m+1));
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= m; ++j){
dp[i][j] = dp[i - 1][j] + (j>i ? dp[i - 1][j - i] : 0) + (i == j ? 1 : 0);
}
}
return dp[n][m];
}