为了解决这个问题,我们需要修改一个记账单字符串,使其满足两个条件:账户在任意时刻不出现负数,且最终余额为指定值。记账单由 '+' 和 '-' 组成,分别表示存入和取出1元钱。初始余额为 p,最终余额需为 q。允许的操作包括取反某一位(耗时 x)和循环右移(耗时 y),目标是找到最小耗时。 ### 方法思路 1. **问题分析**:记账单长度为 n,每个字符为 '+' 或 '-'。初始余额 p,最终余额 q。总净变化量('+' 数减 '-' 数)必须等于 q - p。若当前总净变化量 D0 不等于 q - p,则需修改字符,每次修改(取反)会使总净变化量增加或减少 2。 2. **循环移位处理**:通过循环移位 k 次,将字符串后 k 个字符移到前面。枚举所有可能的 k(0 到 n-1),对每个 k 生成新字符串。 3. **字符修改**: - 若 Δ = (q - p) - D0 ≥ 0,需将 m = Δ / 2 个 '-' 改为 '+'。 - 若 Δ < 0,需将 m = |Δ| / 2 个 '+' 改为 '-'。 4. **非负条件检查**:对每个移位后的字符串,检查修改后任意时刻余额非负: - Δ ≥ 0 时,优先修改靠前的 '-',计算修改后的最小余额。 - Δ < 0 时,优先修改靠后的 '+',计算修改后的最小余额。 5. **耗时计算**:对每个有效 k,计算总耗时(k*y + m*x),并取最小值。 ### 解决代码 ```cpp #include #include #include #include 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 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,计算总耗时(移位耗时 k*y + 修改耗时 m*x),并更新最小值。 6. **输出结果**:输出最小耗时。 此方法通过枚举所有可能的循环移位,并高效检查修改后的有效性,确保在较大数据规模下(n ≤ 9000)也能在合理时间内求解。