动态规划经典例题

动态规划

走楼梯

楼梯有N级台阶,上楼可以一步上一阶,也可以一步上二阶。编一递归程序,计算共有多少种不同走法?

输入

输入N (1 <= n <=36)

输出

输出多少种走法

样例输入 3

样例输出 3

#include <bits/stdc++.h>
using namespace std;

int Upstair(int N) {
    if (N == 1) {
        return 1;
    }
    if (N == 2) {
        return 2;
    }
    int prev2 = 1; // 对应于N==1的情况
    int prev1 = 2; // 对应于N==2的情况
    int current = 0;
    for (int i = 3; i <= N; i++) {
        current = prev1 + prev2; // 当前值是前两个值的和
        prev2 = prev1; // 更新倒数第二个值
        prev1 = current; // 更新倒数第一个值
    }
    return current;
}

int main() {
    int N;
    cin >> N;
    cout << Upstair(N) << endl;
}

01背包问题

给定n(n<=100)种物品和一个背包。物品i的重量是wi(wi<=100),价值为vi(vi<=100),背包的容量为C(C<=1000)。
应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。

共有n+1行输入:
第一行为n值和c值,表示n件物品和背包容量c;
接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。

输出格式:

输出装入背包中物品的最大总价值。

输入样例:

在这里给出一组输入。例如:

5 10
2 6
2 3
6 5
5 4
4 6

输出样例:

15

#include <iostream>
#include <vector>
using namespace std;
int knapsack(int n, int C, vector<pair<int, int>>& items) {
    // dp[i][w] 表示在前i个物品中,容量为w的背包所能装下的最大价值
    vector<vector<int>> dp(n + 1, vector<int>(C + 1, 0));

    for (int i = 1; i <= n; ++i) {
        for (int w = 1; w <= C; ++w) {
            // 不放入第i个物品
            dp[i][w] = dp[i - 1][w];
            // 放入第i个物品,前提是当前背包容量能够容纳该物品
            if (items[i - 1].first <= w) {
                dp[i][w] = max(dp[i][w], dp[i - 1][w - items[i - 1].first] + items[i - 1].second);
            }
        }
    }
    // 返回背包能装下的最大价值
    return dp[n][C];
}

int main() {
    int n, C;
    cin >> n >> C;
    vector<pair<int, int>> items(n);
    for (int i = 0; i < n; ++i) {
        cin >> items[i].first >> items[i].second;
    }

    cout << knapsack(n, C, items) << endl;
    return 0;
}

最长公共子串

输入格式:

首先输入一个整数T,表示测试数据的组数,然后是T组测试数据。每组测试数据在第一行中输入主串s,在第二行中输入子串t,s和t中不包含空格。

输出格式:

对于每组测试,输出两行,第一行是最长公共子串的长度,第二行是最长公共子串(以第一个串中字符的出现次序优先,参看输出样例)。

输入样例:

2
abcfbc
abfcab
abfcab
abcfbc

输出样例:

4
abcb
4
abfc

#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 用于计算最长公共子序列的长度和内容
void LCS(const string &s, const string &t, int &length, string &lcs)
{
    int m = s.size(), n = t.size();
    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 (s[i - 1] == t[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    length = dp[m][n]; // 最长公共子序列的长度
    // 通过dp数组回溯找到最长公共子序列
    lcs = "";
    int i = m, j = n;
    while (i > 0 && j > 0)
    {
        if (s[i - 1] == t[j - 1])
        {
            lcs = s[i - 1] + lcs; // 当前字符是LCS的一部分
            --i;
            --j;
        }
        else if (dp[i - 1][j] >= dp[i][j - 1])
        { // 优先考虑第一个字符串
            --i;
        }b
        else
        {
            --j;
        }
    }
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        string s, t;
        cin >> s >> t;
        int length;
        string lcs;
        LCS(s, t, length, lcs);
        cout << length << endl;
        cout << lcs << endl;
    }
    return 0;
}

矩阵连乘问题

输入格式:

输入有两行。第一行一个n表示矩阵的个数;第二行有n+1个数,分别为p0,p1…pn

输出格式:

一个数,表示最少的乘法次数。

输入样例:

6

30 35 15 5 10 20 25

输出样例:

15125

#include <bits/stdc++.h>
using namespace std;
int matrixChainOrder(vector<int> &p, int n)
{
    vector<vector<int>> dp(n, vector<int>(n, 0));
    for (int L = 2; L < n; L++)
    {
        for (int i = 1; i < n - L + 1; i++)
        {
            int j = i + L - 1;
            dp[i][j] = INT_MAX;
            for (int k = i; k <= j - 1; k++)
            {
                int q = dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j];
                if (q < dp[i][j])
                    dp[i][j] = q;
            }
        }
    }

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

int main()
{
    int n;
    cin >> n;
    vector<int> p(n + 1);
    for (int i = 0; i <= n; i++)
    {
        cin >> p[i];
    }

    cout << matrixChainOrder(p, n + 1) << endl;

    system("pause");
}

状态压缩+动态规划/DFS

编写一个程序,确定哪些矮人是合法的,也就是说,从九个数字中选择七个和为100的数字。

输入格式:

共有9行输入。每个包含一个介于1和99(含)之间的整数。所有的数字都是不同的。
注意:测试数据是唯一的。

输出格式:

你的程序必须精确地产生七行输出——白雪公主七个小矮人帽子上的数字。按任意顺序输出数字。

输入样例1:

7
8
10
13
15
19
20
23
25

输出样例1:

7
8
10
13
19
20
23

#include <iostream>
#include <vector>
using namespace std;

vector<int> dwarfs(9); // 创建一个长度为9的向量,用于存储九个矮人的帽子数
vector<int> solution; // 用于存储符合条件的七个矮人的帽子数

// 使用深度优先搜索(DFS)算法寻找符合条件的七个矮人
// index: 当前考虑的矮人索引
// count: 已选矮人的数量
// sum: 已选矮人帽子数的总和
// state: 用位表示的当前已选矮人的状态,每一位代表一个矮人是否被选中
bool dfs(int index, int count, int sum, int state)
{
    // 如果已选了7个矮人且它们的帽子数之和为100,则找到一个解
    if (count == 7 && sum == 100)
    {
        // 遍历所有矮人,将符合条件的矮人的帽子数加入solution向量
        for (int i = 0; i < 9; ++i)
        {
            if (state & (1 << i)) // 使用位操作检查矮人是否被选中
            {
                solution.push_back(dwarfs[i]);
            }
        }
        return true;
    }
    // 如果已遍历所有矮人或者已选矮人数超过7或帽子数之和超过100,则回溯
    if (index == 9 || count > 7 || sum > 100)
    {
        return false;
    }
    // 尝试选择当前矮人并递归搜索
    if (dfs(index + 1, count + 1, sum + dwarfs[index], state | (1 << index)))
    {
        return true;
    }
    // 不选择当前矮人并递归搜索
    if (dfs(index + 1, count, sum, state))
    {
        return true;
    }
    // 如果以上两种情况都不成功,返回false进行回溯
    return false;
}

int main()
{
    // 读取九个矮人的帽子数
    for (int i = 0; i < 9; ++i)
    {
        cin >> dwarfs[i];
    }
    // 从第0个矮人开始搜索解决方案
    dfs(0, 0, 0, 0);

    // 输出找到的解决方案,即符合条件的七个矮人的帽子数
    for (int num : solution)
    {
        cout << num << endl;
    }

    return 0;
}

数位DP问题

Bozˇo是个奇怪的小男孩。他每天都用奇怪的问题来烦他的朋友。今天的问题是:区间[AB]中有多少个整数的位数之和是s,哪个是最小的?
写一个程序来回答Bozˇo的问题,这样他就可以睡一觉了。

输入格式:

输入包含三个整数ABS(1≤AB<1015,1≤S≤135).

输出格式:

第一行应该包含区间中的整数个数,其数字和等于S。
第二行应该包含这样的最小整数。
输入数据将保证第一个数字至少为1

得分

正确输出两个数字中的一个,你将获得50%的分数。
注意:如果你只想得到第二个数字的分数,一定要输出一些东西(例如0)作为第一个数字,这样判题系统才能正确解释你的输出。

输入样例1:

1 9 5

输出样例1:

1
5

输入样例2:

1 100 10

输出样例2:

9
19

输入样例3:

11111 99999 24

输出样例3:

5445
11499
#include <bits/stdc++.h>
using namespace std;

const int MAX_DIGITS = 20;            // 由于范围很大,我们可能需要更多的数位
const int MAX_SUM = 165;              // 数位之和的最大值,9*18(考虑到最多18位数字)
long long dp[MAX_DIGITS][2][MAX_SUM]; // DP数组:位置、限制标记、已经累积的数位和

// 将数字转换为数位数组
vector<int> toDigits(long long x)
{
    vector<int> digits;
    while (x)
    {
        digits.push_back(x % 10);
        x /= 10;
    }
    reverse(digits.begin(), digits.end());
    return digits;
}

// 计算一个数字的数位和
int digitSum(long long x)
{
    int sum = 0;
    while (x)
    {
        sum += x % 10;
        x /= 10;
    }
    return sum;
}
long long solve(const vector<int> &digits, size_t pos, int tight, int sum)
{
    if (pos == digits.size())
        return sum == 0;
    if (sum < 0)
        return 0;
    if (!tight && dp[pos][0][sum] != -1)
        return dp[pos][0][sum];
    if (tight && dp[pos][1][sum] != -1)
        return dp[pos][1][sum];

    long long ans = 0;
    int limit = tight ? digits[pos] : 9;
    for (int d = 0; d <= limit; ++d)
    {
        ans += solve(digits, pos + 1, tight && d == limit, sum - d);
    }

    dp[pos][tight][sum] = ans;
    return ans;
}
long long countSolve(long long n, int s)
{
    memset(dp, -1, sizeof(dp));
    vector<int> digitsN = toDigits(n);
    return solve(digitsN, 0, 1, s);
}

long long findMinNumberBinarySearch(long long a, long long b, int s)
{
    long long left = a, right = b, mid, res = -1;
    while (left <= right)
    {
        mid = left + (right - left) / 2;
        long long countMid = countSolve(mid, s);
        long long countA = countSolve(a - 1, s);

        if (countMid - countA > 0)
        {
            res = mid;       // Found a candidate
            right = mid - 1; // Try to find a smaller number
        }
        else
        {
            left = mid + 1;
        }
    }
    return res; // This will be the smallest number satisfying the condition
}

int main()
{
    long long a, b;
    int s;
    cin >> a >> b >> s;

    memset(dp, -1, sizeof(dp)); // 初始化DP数组
    vector<int> digitsB = toDigits(b);
    long long countB = solve(digitsB, 0, 1, s);

    memset(dp, -1, sizeof(dp)); // 重置DP数组
    vector<int> digitsA = toDigits(a - 1);
    long long countA = solve(digitsA, 0, 1, s);

    cout << countB - countA << endl; // 输出满足条件的数字数量

    long long minNumber = findMinNumberBinarySearch(a, b, s);
    cout << minNumber << endl;
    return 0;
}

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值