ACM寒假集训第六天 代码及其思路

ACM集训代码解析与思路分享

一、

1.代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int maxSubArray(vector<int>& nums) {
    if (nums.empty()) return 0;

    int max_current = nums[0];
    int max_global = nums[0];

    for (int i = 1; i < nums.size(); ++i) {
        max_current = max(nums[i], max_current + nums[i]);
        if (max_current > max_global) {
            max_global = max_current;
        }
    }

    return max_global;
}

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

    int result = maxSubArray(nums);
    cout << result << endl;

    return 0;
}

2.思路

  1. 初始化:设置两个变量,max_current 和 max_global,分别用于存储当前子数组的最大和以及全局最大和。初始时,它们都可以设置为数组的第一个元素。

  2. 遍历数组:从数组的第二个元素开始遍历。对于每个元素,更新max_current为当前元素本身或当前元素加上max_current中的较大值。这表示要么从当前元素开始新的子数组,要么继续扩展当前的子数组。

  3. 更新全局最大值:在每次更新max_current后,如果max_current大于max_global,则更新max_global

  4. 返回结果:遍历结束后,max_global即为最大子数组的和。

二、

1.代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int T, M;
    cin >> T >> M;
    vector<int> time(M), value(M);
    for (int i = 0; i < M; ++i) {
        cin >> time[i] >> value[i];
    }

    vector<vector<int>> dp(M + 1, vector<int>(T + 1, 0));

    for (int i = 1; i <= M; ++i) {
        for (int j = 0; j <= T; ++j) {
            if (j >= time[i-1]) {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-time[i-1]] + value[i-1]);
            } else {
                dp[i][j] = dp[i-1][j];
            }
        }
    }

    cout << dp[M][T] << endl;

    return 0;
}

2.思路

  1. 定义状态:我们使用一个二维数组dp[i][j]来表示前i种草药在时间j内可以获得的最大价值。

  2. 状态转移方程

    • 如果不选择第i种草药,那么dp[i][j] = dp[i-1][j]

    • 如果选择第i种草药,那么dp[i][j] = dp[i-1][j-time[i]] + value[i],其中time[i]是采摘第i种草药所需的时间,value[i]是第i种草药的价值。

    • 最终,dp[i][j]取上述两种情况的最大值。

  3. 初始化dp[0][j] = 0,表示没有草药时,无论有多少时间,总价值都是0。

  4. 结果dp[M][T]即为在时间T内可以获得的最大价值。

三、

1.代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int n, W;
    cin >> n >> W;
    vector<int> v, w;
    for (int i = 0; i < n; ++i) {
        int vi, wi, mi;
        cin >> vi >> wi >> mi;
        // Binary splitting
        for (int k = 1; k <= mi; k *= 2) {
            v.push_back(vi * k);
            w.push_back(wi * k);
            mi -= k;
        }
        if (mi > 0) {
            v.push_back(vi * mi);
            w.push_back(wi * mi);
        }
    }

    vector<int> dp(W + 1, 0);
    for (int i = 0; i < v.size(); ++i) {
        for (int j = W; j >= w[i]; --j) {
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }
    }

    cout << dp[W] << endl;

    return 0;
}

2.思路

  1. 定义状态:我们使用一个一维数组dp[j]来表示在载重为j的情况下可以获得的最大价值。

  2. 状态转移方程

    • 对于每种宝物,我们考虑选择0到m_i件该宝物。

    • 对于每种可能的载重j,我们更新dp[j]为选择当前宝物和不选择当前宝物中的最大值。

  3. 优化:为了减少时间复杂度,可以使用二进制拆分的方法将多重背包问题转化为0-1背包问题。具体来说,将每种宝物的数量m_i拆分成若干个2的幂次方的和,然后将这些拆分后的宝物视为独立的0-1背包问题。

四、

1.代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> p1(n), p2(n);
    for (int i = 0; i < n; ++i) {
        cin >> p1[i];
    }
    for (int i = 0; i < n; ++i) {
        cin >> p2[i];
    }

    // Create a position array for p2
    vector<int> pos(n + 1);
    for (int i = 0; i < n; ++i) {
        pos[p2[i]] = i;
    }

    // Convert p1 to a sequence of positions in p2
    vector<int> seq(n);
    for (int i = 0; i < n; ++i) {
        seq[i] = pos[p1[i]];
    }

    // Find the length of the longest increasing subsequence in seq
    vector<int> dp;
    for (int i = 0; i < n; ++i) {
        auto it = lower_bound(dp.begin(), dp.end(), seq[i]);
        if (it == dp.end()) {
            dp.push_back(seq[i]);
        } else {
            *it = seq[i];
        }
    }

    cout << dp.size() << endl;

    return 0;
}

2.思路

  1. 位置映射:首先,我们为第二个排列创建一个位置数组pos,其中pos[i]表示数字i在第二个排列中的位置。

  2. 转换排列:将第一个排列转换为一个新的序列seq,其中每个元素是其在第二个排列中的位置。

  3. 寻找LIS:在新的序列seq中寻找最长递增子序列(LIS),其长度即为两个排列的最长公共子序列的长度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值