alg2025/src/7/20/T371062.md
2025-07-20 11:49:10 +08:00

4.8 KiB
Raw Blame History

为了解决这个问题,我们需要修改一个记账单字符串,使其满足两个条件:账户在任意时刻不出现负数,且最终余额为指定值。记账单由 '+' 和 '-' 组成分别表示存入和取出1元钱。初始余额为 p最终余额需为 q。允许的操作包括取反某一位耗时 x和循环右移耗时 y目标是找到最小耗时。

方法思路

  1. 问题分析:记账单长度为 n每个字符为 '+' 或 '-'。初始余额 p最终余额 q。总净变化量'+' 数减 '-' 数)必须等于 q - p。若当前总净变化量 D0 不等于 q - p则需修改字符每次修改取反会使总净变化量增加或减少 2。
  2. 循环移位处理:通过循环移位 k 次,将字符串后 k 个字符移到前面。枚举所有可能的 k0 到 n-1对每个 k 生成新字符串。
  3. 字符修改
    • 若 Δ = (q - p) - D0 ≥ 0需将 m = Δ / 2 个 '-' 改为 '+'。
    • 若 Δ < 0需将 m = |Δ| / 2 个 '+' 改为 '-'。
  4. 非负条件检查:对每个移位后的字符串,检查修改后任意时刻余额非负:
    • Δ ≥ 0 时,优先修改靠前的 '-',计算修改后的最小余额。
    • Δ < 0 时,优先修改靠后的 '+',计算修改后的最小余额。
  5. 耗时计算:对每个有效 k计算总耗时ky + mx并取最小值。

解决代码

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

int main() {
    long long n, p, q, x, y;
    cin >> n >> p >> q >> x >> y;
    string s;
    cin >> s;

    long long D0 = 0;
    for (char c : s) {
        if (c == '+') D0++;
        else D0--;
    }
    long long d = q - p;
    long long delta = d - D0;
    long long m_val = abs(delta) / 2;

    long long ans = 1e18;
    vector<int> h_arr(n + 2, 0);

    for (int k = 0; k < n; k++) {
        if (delta >= 0) {
            long long current = p;
            long long f = 0;
            long long min_value = p;
            for (int i = 1; i <= n; i++) {
                int index_s = n - k + i - 1;
                if (index_s >= n) index_s -= n;
                char c_char = s[index_s];
                if (c_char == '+') {
                    current++;
                } else {
                    current--;
                    f++;
                }
                long long c_i = min(m_val, f);
                long long value = current + 2 * c_i;
                if (value < min_value) min_value = value;
            }
            if (min_value >= 0) {
                long long cost = 1LL * k * y + 1LL * m_val * x;
                if (cost < ans) ans = cost;
            }
        } else {
            for (int i = 0; i <= n + 1; i++) h_arr[i] = 0;
            for (int i = n - 1; i >= 0; i--) {
                int index_s = n - k + i;
                if (index_s >= n) index_s -= n;
                h_arr[i] = h_arr[i + 1] + (s[index_s] == '+' ? 1 : 0);
            }
            long long current = p;
            long long min_value = current - 2 * m_val + 2 * min(m_val, (long long)h_arr[0]);
            for (int i = 1; i <= n; i++) {
                int index_s = n - k + i - 1;
                if (index_s >= n) index_s -= n;
                if (s[index_s] == '+') {
                    current++;
                } else {
                    current--;
                }
                long long value = current - 2 * m_val + 2 * min(m_val, (long long)h_arr[i]);
                if (value < min_value) min_value = value;
            }
            if (min_value >= 0) {
                long long cost = 1LL * k * y + 1LL * m_val * x;
                if (cost < ans) ans = cost;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

代码解释

  1. 输入处理:读取记账单长度 n、初始余额 p、目标余额 q、操作耗时 x 和 y以及记账单字符串 s。
  2. 总净变化量计算:遍历字符串,计算初始总净变化量 D0'+' 数减 '-' 数)。
  3. 计算 Δ 和 m:Δ = (q - p) - D0若 Δ 非负则 m = Δ / 2需修改 m 个 '-' 为 '+'),否则 m = |Δ| / 2需修改 m 个 '+' 为 '-')。
  4. 枚举循环移位次数 k:对每个 k 生成移位后的字符串,检查修改后是否满足非负条件:
    • Δ ≥ 0 时:遍历字符串,计算修改后的最小余额,检查是否非负。
    • Δ < 0 时:预处理后缀 '+' 数,计算修改后的最小余额,检查是否非负。
  5. 计算最小耗时:对满足条件的 k计算总耗时移位耗时 ky + 修改耗时 mx并更新最小值。
  6. 输出结果:输出最小耗时。

此方法通过枚举所有可能的循环移位并高效检查修改后的有效性确保在较大数据规模下n ≤ 9000也能在合理时间内求解。