删除顺序等价于每次删第一个下降位;用单调不减栈一次扫描,弹出次数正好等于删除名额,剩余名额删末尾,时间 O(L)。
OJ: roj
题目 ID: 1321
难度:普及
标签:字符串贪心单调栈python
创建: 2026-09-30 05:05
更新: 2026-10-05 09:41
题目描述
输入一个高精度正整数
输入两行:第一行为
输出最后剩下的最小数(若剩下的数字全是 0,输出 0)。
input
175438
4output
13思路
剩下的位数固定为
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 05:05
# update_at: 2026-09-30 05:08
import sys
def smallest_after_deletions(digits: str, s: int) -> str:
"""在 digits 中删去 s 位,返回剩下数字串能组成的正整数(去掉前导零)。
维护一个单调不减的栈:新来的一位如果比栈顶小,就说明"栈顶那位靠左却更大",
删掉它一定比删掉当前位更划算,于是反复弹栈;扫完还有删除余量时,
栈已经不减,剩下的名额只能从末尾花掉。
"""
kept: list[str] = []
budget = s # 还剩几个删除名额
for digit in digits:
while budget and kept and kept[-1] > digit: # 左侧更大且更靠前,删它
kept.pop()
budget -= 1
kept.append(digit)
if budget: # kept 此时单调不减,末尾几位最大,删它们最划算
del kept[len(kept) - budget:]
return ''.join(kept).lstrip('0') or '0' # 全 0(或精光)时题面要求输出 0
def solve() -> None:
data = iter(sys.stdin.read().split())
n = next(data)
s = int(next(data))
print(smallest_after_deletions(n, s))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 09:42
* update_at: 2026-10-05 09:42
*/
#include <cstdio>
#include <cstring>
typedef long long ll;
const int MAXL = 250; // n 不超过 240 位
char num[MAXL]; // 输入的数字串
char stk[MAXL]; // 单调不减栈:stk[0..top-1] 是当前确定保留的前缀
ll top; // 栈顶指针(栈内元素个数)
ll s; // 还能删多少个数字(删除名额)
int main() {
scanf("%s", num);
scanf("%lld", &s);
ll len = strlen(num);
// 贪心:读入新数字 c 时,栈顶比 c 大说明栈顶那位靠左却更大,
// 删掉它一定更优,弹出并消耗一个删除名额,直到栈顶不大于 c 或名额用完
top = 0;
for (ll i = 0; i < len; i++) {
char c = num[i];
while (s > 0 && top > 0 && stk[top - 1] > c) {
top--; // 弹出栈顶 = 删掉一个数字
s--;
}
stk[top] = c;
top++;
}
// 扫描结束后串已单调不减,名额没用完就从末尾(最大的几位)继续删
top -= s;
s = 0;
// 剥掉前导零;剩下的字符全是 0 时输出 0
ll start = 0;
while (start < top - 1 && stk[start] == '0')
start++;
for (ll i = start; i < top; i++)
putchar(stk[i]);
putchar('\n');
return 0;
}