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