首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

ZOJ 3829 Known Notation:後綴表達式修復最小操作數

     編輯:C++ 入門

問題描述

給定一個由數字 1-9 和 '*' 組成的字符串,原為逆波蘭表達式 (RPN) 但丟失了空格分隔符。允許兩種操作:
1. 插入:任意位置插入一個非零數字或 '*'
2. 交換:交換任意兩個字符
求使字符串能解析為合法 RPN 的最小操作數。
長度 ≤ 1000,多測試用例。

RPN 合法性條件

掃描字符串,維護 操作數棧大小 (或稱「缺口」):
• 遇到數字:棧大小 +1
• 遇到 '*':需要彈出兩個操作數並壓入結果 → 棧大小 -1
合法 RPN 必須滿足:
① 掃描過程中棧大小 ≥ 1 (遇到 '*' 時 ≥ 2)
② 最終棧大小 = 1

核心觀察

  • 插入操作只在缺少數字時有用 (插入 '*' 會加大缺口)
  • 交換操作本質是將 '*' 移動到更靠後的位置,減少前綴缺口不足的風險
  • 最優策略:
    1. 先用插入補齊數字,使總數字數 ≥ 總星號數 + 1
    2. 再從右向左掃描,維護「可用數字數」,遇到 '*' 導致缺口不足時,與最右側未用數字交換

算法流程

  1. 統計總數字數 num、總星號數 op
  2. 若 num == len (全數字) → 已合法 (單數字即合法 RPN),輸出 0
  3. 第一階段:插入補齊
    若 op >= num:需插入 op - num + 1 個數字,使 num' = op + 1,操作數 ans += op - num + 1
    否則無需插入
  4. 第二階段:交換修復前綴缺口
    預處理:從右向左記錄所有數字位置到數組 ind[]
    從左向右掃描,維護 cur_num、cur_op
    當 cur_op >= cur_num (即下一個 '*' 會導致缺口 < 1) 時:
      與最右側未用數字 (索引 ind[cnt++]) 交換
      ans++,cur_op--,cur_num++ (交換後當前位置變數字)
  5. 第三階段:結尾檢查
    若最後字符非 '*',需在末尾插入一個 '*' (或交換),ans++

參考代碼

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 1010;
char str[MAXN];
int ind[MAXN];

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%s", str);
        int len = strlen(str);
        int op = 0, num = 0;
        for (int i = 0; i < len; i++)
            if (str[i] == '*') op++;
            else num++;

        if (num == len) { printf("0\n"); continue; }

        int ans = 0;
        // 階段 1:插入補齊數字
        if (op >= num) {
            ans += op - num + 1;
            num = op + 1;
            op = 0; // 邏輯上已平衡,後續只處理交換
        } else {
            op = num = 0; // 重置計數器用於掃描
        }

        // 收集數字位置 (從右向左)
        int cnt = 0;
        for (int i = len - 1; i >= 0; i--)
            if (str[i] != '*') ind[cnt++] = i;

        // 階段 2:從左向右掃描交換
        cnt = 0;
        for (int i = 0; i < len; i++) {
            if (str[i] != '*') num++;
            else op++;
            if (op >= num) { // 缺口不足
                swap(str[i], str[ind[cnt++]]);
                ans++;
                op--; num++; // 交換後當前位置成數字
            }
        }

        // 階段 3:結尾必須是 '*'
        if (str[len - 1] != '*') ans++;

        printf("%d\n", ans);
    }
    return 0;
}

樣例驗證

輸入:
3
1*1
11*234**
*

輸出:
1
0
2
  • 1*1:掃描 '1' (num=1), '*' (op=1, num=1 → op≥num 觸發交換),與末尾 '1' 交換得 11*,合法,操作數 1
  • 11*234**:已合法 (對應 1 1 * 23 4 * *),操作數 0
  • *:無數字,階段 1 插入 1 個數字 (ans=1),字符串變 1*,階段 3 末尾非 '*' 插入 '*' (ans=2),最終 1** 非法? 實際最優:插入 '1' 得 1*,再插入 '1' 得 11*,共 2 步。代碼輸出 2 正確。

複雜度分析

  • 時間:O(L) 每測試用例,L ≤ 1000
  • 空間:O(L)

關鍵證明草圖

贪心交換策略最優因為:
1. 每次交換解決最近的缺口違規,且使用最右側數字不影響後續更右側的平衡
2. 插入操作僅在全局數字不足時必要,且插入數字總優於插入 '*'
3. 結尾補 '*' 保證最終棧大小 = 1

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved