104 lines
4.8 KiB
Markdown
104 lines
4.8 KiB
Markdown
为了解决这个问题,我们需要修改一个记账单字符串,使其满足两个条件:账户在任意时刻不出现负数,且最终余额为指定值。记账单由 '+' 和 '-' 组成,分别表示存入和取出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 <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,计算总耗时(移位耗时 k*y + 修改耗时 m*x),并更新最小值。
|
||
6. **输出结果**:输出最小耗时。
|
||
|
||
此方法通过枚举所有可能的循环移位,并高效检查修改后的有效性,确保在较大数据规模下(n ≤ 9000)也能在合理时间内求解。 |