4.1.1 蓝桥杯之动态规划线性DP课后习题建造房屋

.1.1

问题描述

小蓝和小桥是两位年轻的建筑师,他们正在设计一座新的城市。

在这个城市中,有N条街道,每条街道上都有M个位置可以建造房屋(一个位置只能建造一个房屋)。建造一个房屋的费用为1元,小蓝和小桥共有K元的建造预算。

现在,他们想知道,一共有多少种建造方案,满足以下要求:

·在每条街道上,至少建一个房屋。

·建造的总成本不能超过K元。由于方案数可能很大,他们只需要输出答案对10°+7取模的结果。

输入格式

一行三个整数N,M(1≤N,M≤30)和K(1≤K≤N·

M),分别表示街道数、街道的位置数和预算。

输出格式

一个整数,表示满足条件的建造方案数对10⁹+7取模的结果。

样例输入

235

样例输出

8

我的答案:

一、信息

  • 问题描述: 小蓝和小桥需要设计城市中的房屋建造方案。
  • 条件:
    • 城市有 N条街道。
    • 每条街道有 M个位置 可建造房屋。
    • 每个位置 只能建造一个房屋,建造费用为 1元。
    • 总预算为 K元。
    • 要求: 每条街道至少建一个房屋,总成本不超过K元。
  • 目标: 计算满足条件的建造方案数,结果对 10^9 + 7 取模。
  • 输入: N, M, K
  • 输出: 方案数 % (10^9 + 7)
  • 示例输入: 2, 3, 5
  • 示例输出: 8

二、分析

  • 每个信息的作用:
    • N, M定义了问题的规模。
    • K限制了解空间,即方案数的计算。
  • 思考和分析过程:
    • 这是一个典型的 动态规划问题。
    • 需要考虑在预算限制下,如何在每条街道上至少建一个房屋。
    • 可以通过 组合计数 来求解。

三、算法设计

  • 动态规划:
    • 定义状态 dp[i][j] 表示在前i条街道上,使用j元预算的方案数。
    • 状态转移方程: dp[i][j] = sum(dp[i-1][j-k]) 其中 k 从1到 min(M, j)。

四、代码实现(C++)

#include 
#include 
using namespace std;

const int MOD = 1e9 + 7;

int countBuildingPlans(int N, int M, int K) {
    vector<vector> dp(N + 1, vector(K + 1, 0));
    dp[0][0] = 1; // 初始化

    for (int i = 1; i <= N; ++i) {
        for (int j = 1; j <= K; ++j) {
            for (int k = 1; k > N >> M >> K;
    cout << countBuildingPlans(N, M, K) << endl;
    return 0;
}

五、实现代码过程中可能遇到的问题

  • 内存限制: 给定的N和M可能导致dp数组过大。
  • 时间复杂度: 三重循环可能导致时间超限,需要考虑优化算法。
  • 精度问题: 在模运算中需要注意避免溢出。

蓝桥杯官方题解:

.1.1

一、信息

  • 问题描述: 设计一个城市建筑方案,满足预算和建造要求。
  • 条件:
    • N条街道,每条街道上有 M个位置。
    • 每个位置建造费用为 1元。
    • 总预算 K元。
    • 要求每条街道至少建一个房屋,总成本不超过K元。
  • 目标: 计算符合条件的建造方案数,结果对 10^9 + 7 取模。
  • 输入: N, M, K
  • 输出: 方案数 % (10^9 + 7)

二、分析

  • 每个信息的作用:
    • N, M, K定义了问题的规模和限制。
  • 思考和分析过程:
    • 使用 动态规划。
    • 定义状态 dp[i][j] 表示前i条街道上建造j个房屋的方案数。
    • 每条街道至少建一个房屋,基准点设置。
    • 考虑在不超过预算下建造1到m个房屋的情况。

三、算法设计

  • 动态规划:
    • 状态定义:dp[i][j] 表示前i条街道上建造j个房屋的方案数。
    • 状态转移方程:dp[i][j+k] = (dp[i][j+k] + dp[i-1][j]) % MOD。
    • 初始化:dp[0][i] = 1。

四、代码实现(C++)

#include
using namespace std;

int n , m , k , ans , mod = 1e9 + 7;
long long dp[55][2605];

signed main() {
    cin >> n >> m >> k;
    for(int i = 0; i <= k; i++) dp[0][i] = 1;

    for(int i = 0; i < n; i++)
        for(int j = 1; j <= m; j++)
            for(int l = i; l <= k; l++)
                (dp[i + 1][j + l] += dp[i][l]) %= mod;

    cout << dp[n][k] << '\n';
    return 0;
}

五、实现代码过程中可能遇到的问题

  • 内存限制: 给定的N和M可能导致dp数组过大,需要注意内存的使用。
  • 时间复杂度: 三重循环可能导致时间超限,需要考虑优化算法。
  • 精度问题: 在模运算中需要注意避免溢出。

六、总结:

  1. 动态规划(Dynamic Programming):

    • 动态规划是解决具有重叠子问题和最优子结构特性的问题的一种方法。在这个问题中,我们通过构建一个表格(dp数组)来避免重复计算相同的子问题,并利用子问题的解来构建原问题的解。
  2. 状态定义和转移:

    • 动态规划的核心在于定义“状态”并找出状态之间的转移关系。这个问题教会我们如何精确定义状态(这里是 dp[i][j] 表示前i条街道上建造j个房屋的方案数),以及如何根据问题的限制(每条街道至少建一个房屋,总预算不超过K元)来确定状态转移方程。
  3. 组合数学:

    • 此问题还涉及到基本的组合数学概念,因为我们在计算不同街道上建造不同数量房屋的所有可能组合。
  4. 模运算和大数处理:

    • 在实际编程中,处理大数并进行模运算是常见的。这个问题展示了如何在计算过程中使用模运算来避免整数溢出。
  5. 算法的优化:

    • 这个问题也可以引导我们思考如何优化算法以处理更大规模的数据,比如通过减少不必要的计算,或改进数据结构。
  6. 实际应用:

    • 尽管这是一个抽象的问题,但它模拟了实际生活中的资源分配和规划问题,例如预算规划、建筑设计等。

.1.1

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/dfeae275fc.html