题目描述
小明每周上班都会拿到自己的工作清单,工作清单内包含n项工作,每项工作都有对应的耗时时长(单位h)和报酬,工作的总报酬为所有已完成工作的报酬之和。那么请你帮小明安排一下工作,保证小明在指定的工作时间内工作收入最大化。

输入描述
输入的第一行为两个正整数工 T,n。T 代表工作时长(单位h,0

接下来是n行,每行包含两个整数t,w。t代表该项工作消耗的时长(单位h,t>0),w代表该项工作的报酬。

输出描述
输出小明指定工作时长内工作可获得的最大报酬。

#include 
#include 
#include 

using namespace std;

int main() {
    int T, n;
    cin >> T >> n; // 读取工作时长和工作数量

    // 创建一个二维数组来存储动态规划的结果
    vector<vector<int>> dp(T + 1, vector<int>(n + 1, 0));

    // 读取每项工作的时间和报酬
    for (int i = 1; i <= n; ++i) {
        int t, w;
        cin >> t >> w;
        for (int j = T; j >= t; --j) {
            // 如果不选择第i项工作,当前的最大报酬不变
            dp[j][i] = max(dp[j][i-1], dp[j-t][i-1] + w);
        }
    }

    // 输出最大报酬
    cout << dp[T][n] << endl;

    return 0;
}

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">

3.最长公共子序列(Longest Common Subsequence, LCS)

最长公共子序列(Longest Common Subsequence, LCS)问题是计算机科学中的一个经典问题,它要求找出两个序列(通常是字符串)的最长公共子序列,且要求子序列中的元素在原序列中是连续的。

问题描述
给定两个序列 X 和 Y,求它们的最长公共子序列的长度。

#include 
#include 
#include 

using namespace std;

// 动态规划求解最长公共子序列问题
int lcs(const string& X, const string& Y) {
    int m = X.length();
    int n = Y.length();
    vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

    // 构建dp数组
    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;
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }

    return dp[m][n];
}

int main() {
    string X = "ABCBDAB";
    string Y = "BDCABC";

    cout << "最长公共子序列的长度为: " << lcs(X, Y) << endl;

    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

4.最长递增子序列(Longest Increasing Subsequence, LIS)

问题描述:给定一个整数序列 a1, a2, …, an,求其最长递增子序列的长度。
动态规划解法:使用一维数组存储到当前位置为止的最长递增子序列长度。

/*初始化DP数组:创建一个一维数组dp,其中dp[i]表示以a[i]结尾的最长递增子序列的长度。
每个元素至少是1,即自身。
状态转移方程:
对于每个元素a[i],我们检查它之前的所有元素a[j](其中j < i)。
如果a[i] > a[j],则a[i]可以接在a[j]后面形成一个更长的递增子序列,
因此dp[i] = max(dp[i], dp[j] + 1)。
返回结果:最终,dp数组中的最大值即为所求的最长递增子序列的长度。*/
#include 
#include 

using namespace std;

// 动态规划求解最长递增子序列问题
int lis(const vector<int>& a) {
    int n = a.size();
    vector<int> dp(n, 1); // 初始化dp数组,每个元素至少是1(即自身)

    // 构建dp数组
    for (int i = 1; i < n; ++i) {
        for (int j = 0; j < i; ++j) {
            if (a[i] > a[j]) {
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
    }

    // 找出dp数组中的最大值
    int maxLength = 0;
    for (int i = 0; i < n; ++i) {
        maxLength = max(maxLength, dp[i]);
    }

    return maxLength;
}

int main() {
    vector<int> a = {10, 9, 2, 5, 3, 7, 101, 18};

    cout << "最长递增子序列的长度为: " << lis(a) << endl;

    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

5.矩阵链乘问题(Matrix Chain Multiplication)

问题描述:给定一系列的矩阵,求将它们相乘的最小计算量。
动态规划解法:使用三维数组存储不同矩阵组合的最小乘法次数。

/*初始化DP数组:创建一个二维数组dp,其中dp[i][j]表示将矩阵A_i到A_j相乘的最小乘法运算次数。
状态转移方程:
对于每个可能的矩阵链长度len(从2到n),我们考虑所有可能的起始矩阵i和结束矩阵j
(其中j = i + len - 1)。
对于每个可能的分割点k(其中i <= k < j),我们计算将矩阵链分成两部分A_i到A_k和
A_{k+1}到A_j的乘法运算次数,并加上A_i到A_k和A_{k+1}到A_j相乘的乘法运算次数。
选择最小的乘法运算次数作为dp[i][j]的值。
返回结果:最终,dp[1][n]即为所求的最小乘法运算次数,其中n是矩阵的数量。*/
#include 
#include 
#include 

using namespace std;

// 动态规划求解矩阵链乘问题
int matrixChainMultiplication(const vector<int>& p) {
    int n = p.size() - 1;
    vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0));

    // 构建dp数组
    for (int len = 2; len <= n; ++len) {
        for (int i = 1; i <= n - len + 1; ++i) {
            int j = i + len - 1;
            dp[i][j] = INT_MAX;
            for (int k = i; k < j; ++k) {
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j]);
            }
        }
    }

    return dp[1][n];
}

int main() {
    vector<int> p = {30, 35, 15, 5, 10, 20, 25};

    cout << "最小乘法运算次数为: " << matrixChainMultiplication(p) << endl;

    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

6.编辑距离(Edit Distance)

问题描述:给定两个字符串 str1 和 str2,求将 str1 转换为 str2 的最小编辑距离。
动态规划解法:使用二维数组存储两个字符串之间的编辑距离。

#include 
#include 
#include 
#include 

using namespace std;

// 动态规划求解编辑距离问题
int editDistance(const string& str1, const string& str2) {
    int m = str1.length();
    int n = str2.length();
    vector<vector<int>> dp(m + 1, vector<int>(n + 1));

    // 初始化边界条件
    for (int i = 0; i <= m; ++i) {
        dp[i][0] = i; // 将str1的前i个字符删除,得到空字符串
    }
    for (int j = 0; j <= n; ++j) {
        dp[0][j] = j; // 在空字符串中插入j个字符,得到str2的前j个字符
    }

    // 构建dp数组
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (str1[i - 1] == str2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1]; // 当前字符相同,不需要操作
            } else {
                dp[i][j] = 1 + min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}); // 选择最小的操作次数
            }
        }
    }

    return dp[m][n];
}

int main() {
    string str1 = "sunday";
    string str2 = "saturday";

    cout << "最小编辑距离为: " << editDistance(str1, str2) << endl;

    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

7.硬币找零问题(Coin Change Problem)

问题描述:给定不同面额的硬币和总金额,计算组成总金额的最少硬币数量。
动态规划解法:使用一维数组存储达到每个金额所需的最少硬币数量。

/*初始化DP数组:创建一个一维数组 dp,其中 dp[i] 表示组成金额 i 的最少硬币数量。
所有值初始化为最大值 INT_MAX,表示初始状态下无法组成任何金额。
边界条件:dp[0] = 0,表示金额为0时不需要任何硬币。
状态转移方程:
对于每个金额 i(从1到 amount),我们考虑所有可能的硬币面额 coins[j]。
如果 i - coins[j] >= 0 且 dp[i - coins[j]] != INT_MAX,则表示可以使用面额为 coins[j] 的硬币,更新 dp[i] = min(dp[i], dp[i - coins[j]] + 1)。
返回结果:最终,dp[amount] 即为所求的最少硬币数量。如果 dp[amount] 仍为 INT_MAX,则表示无法组成总金额 amount,返回 -1。*/
#include 
#include 
#include 

using namespace std;

// 动态规划求解硬币找零问题
int coinChange(const vector<int>& coins, int amount) {
    vector<int> dp(amount + 1, INT_MAX); // 初始化dp数组,所有值设为最大值
    dp[0] = 0; // 金额为0时,不需要任何硬币

    // 构建dp数组
    for (int i = 1; i <= amount; ++i) {
        for (int j = 0; j < coins.size(); ++j) {
            if (i - coins[j] >= 0 && dp[i - coins[j]] != INT_MAX) {
                dp[i] = min(dp[i], dp[i - coins[j]] + 1);
            }
        }
    }

    return dp[amount] == INT_MAX ? -1 : dp[amount];
}

int main() {
    vector<int> coins = {1, 2, 5};
    int amount = 11;

    cout << "最少硬币数量为: " << coinChange(coins, amount) << endl;

    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

8.最小路径和(Minimum Path Sum)

问题描述:在一个二维矩阵中,从左上角到右下角,只能向右或向下移动,求路径的最小和。
动态规划解法:使用二维数组存储到达每个点的最小路径和。

/*初始化DP数组:创建一个二维数组 dp,
其中 dp[i][j] 表示从左上角到位置 (i, j) 的最小路径和。
边界条件:
dp[0][0] = matrix[0][0],表示起点的路径和就是起点的值。
对于第一行和第一列,只能从左边或上边移动过来,所以路径和是累加的。
状态转移方程:
对于其他位置 (i, j),可以从上面 (i-1, j) 或左边 (i, j-1) 移动过来,
选择较小的路径和,并加上当前位置的值 matrix[i][j]。
返回结果:最终,dp[m-1][n-1] 即为所求的最小路径和,其中 m 和 n 分别是矩阵的行数和列数*/
#include 
#include 

using namespace std;

// 动态规划求解最小路径和问题
int minPathSum(const vector<vector<int>>& matrix) {
    int m = matrix.size();
    int n = matrix[0].size();
    vector<vector<int>> dp(m, vector<int>(n, 0)); // 初始化dp数组

    // 初始化第一行和第一列
    dp[0][0] = matrix[0][0];
    for (int i = 1; i < m; ++i) {
        dp[i][0] = dp[i - 1][0] + matrix[i][0];
    }
    for (int j = 1; j < n; ++j) {
        dp[0][j] = dp[0][j - 1] + matrix[0][j];
    }

    // 构建dp数组
    for (int i = 1; i < m; ++i) {
        for (int j = 1; j < n; ++j) {
            dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + matrix[i][j];
        }
    }

    return dp[m - 1][n - 1];
}

int main() {
    vector<vector<int>> matrix 
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

9.股票买卖问题(Stock Buy and Sell)

问题描述:给定股票价格序列,确定买卖股票的最佳时机以获得最大利润。
动态规划解法:使用一维数组存储每个时间点的最大利润。

/*初始化DP数组:创建一个一维数组 dp,其中 dp[i] 表示前 i 天的最大利润。
所有值初始化为0,表示初始状态下没有利润。
状态转移方程:
对于每一天 i(从1到 n-1),我们比较第 i 天的股票价格和第 i-1 天的股票价格。
如果 prices[i] > prices[i - 1],则表示今天可以卖出股票获得利润,
更新 dp[i] = dp[i - 1] + (prices[i] - prices[i - 1])。
如果 prices[i] <= prices[i - 1],则表示今天卖出股票不会获得利润,
保持 dp[i] = dp[i - 1]。
返回结果:最终,dp[n-1] 即为所求的最大利润,其中 n 是股票价格序列的长度。*/
#include 
#include 

using namespace std;

// 动态规划求解股票买卖问题
int maxProfit(const vector<int>& prices) {
    int n = prices.size();
    if (n <= 1) return 0; // 如果只有一天或没有价格数据,无法获得利润

    vector<int> dp(n, 0); // 初始化dp数组,dp[i]表示前i天的最大利润

    // 构建dp数组
    for (int i = 1; i < n; ++i) {
        if (prices[i] > prices[i - 1]) {
            dp[i] = dp[i - 1] + (prices[i] - prices[i - 1]);
        } else {
            dp[i] = dp[i - 1];
        }
    }

    return dp[n - 1];
}

int main() {
    vector<int> prices = {7, 1, 5, 3, 6, 4};

    cout << "最大利润为: " << maxProfit(prices) << endl;

    return 0;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

10.三步问题
在这里插入图片描述

class Solution {
public:
    int waysToStep(int n) {
            long long dp[1000000]={0};
            dp[1]=1;
            dp[2]=2;
            dp[3]=4;
                for(int i=4;i<=n;i++)
                {
                    dp[i]=(dp[i-1]+dp[i-2]+dp[i-3])%1000000007;
                 }
            return dp[n];
    }
};

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

11.连续数列

在这里插入图片描述

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int dp[100000]={0};
        int ans=nums[0];
        dp[0]=nums[0];
        for(int i=1;i<nums.size();i++)
        {
            dp[i]=max(nums[i],dp[i-1]+nums[i]);
            ans=max(dp[i],ans);
        }
        return ans;
    }
};

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

12.不同路径

在这里插入图片描述

class Solution {
public:
    int uniquePaths(int m, int n) {
        int dp[m][n];
        if(m==0||n==0)
        return 0;
        for(int i=0;i<n;i++)
        {
            dp[0][i]=1;
        }
        for(int i=0;i<m;i++)
        {
            dp[i][0]=1;
        }
        for(int i=1;i<m;i++)
        {
            for(int j=1;j<n;j++)
            {
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
};

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

13.最小路径和

在这里插入图片描述
这题和上题很类似,转移方程也十分的相似,因为只能向右或者向下走,所以我们只要考虑向下和向右走哪个能符合题目条件即可,我们可以将上题的转移方程变式一下,就可以得到:
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int dp[grid.size()][grid[0].size()];
        dp[0][0]=grid[0][0];
        for(int i=1;i<grid.size();i++)
        dp[i][0]=dp[i-1][0]+grid[i][0];
        for(int i=1;i<grid[0].size();i++)
        dp[0][i]=dp[0][i-1]+grid[0][i];
        for(int i=1;i<grid.size();i++)
        {
            for(int j=1;j<grid[0].size();j++)
            {
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[grid.size()-1][grid[0].size()-1];
    }
};

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/weixin_43988686/article/details/144438103","extend1":"pc","ab":"new"}">>
注:本文转载自blog.csdn.net的冰瓜拿铁的文章"https://blog.csdn.net/weixin_43988686/article/details/144438103"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!