删数问题
题目描述
键盘输入一个高精度的正整数 N N N(不超过 250 250 250 位),去掉其中任意 k k k 个数字后剩下的数字按原左右次序将组成一个新的非负整数。编程对给定的 N N N 和 k k k,寻找一种方案使得剩下的数字组成的新数最小。
输入格式
输入两行正整数。
第一行输入一个高精度的正整数 n n n。
第二行输入一个正整数 k k k,表示需要删除的数字个数。
输出格式
输出一个整数,最后剩下的最小数。
样例 #1
样例输入 #1
175438
4
样例输出 #1
13
题解:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
string s; int k; vector<char> st;
int main()
{
cin >> s >> k;
if (s.size() <= k) {
cout << 0;
return 0;
}
for (int i = 0; i < s.size(); i++) {
if (st.empty() || (st[st.size() - 1] <= s[i]) && (st.size() < s.size() - k)) {
st.push_back(s[i]);
}
else if (s[i] < st[st.size() - 1]) {
int l = 0, r = st.size() - 1, mid;
while (l < r) {
mid = (l + r) / 2;
if (st[mid] > s[i])
r = mid;
else
l = mid + 1;
}
if (s.size() - k - r <= s.size() - i) {
while (st.size() >= r + 1)
st.pop_back();
st.push_back(s[i]);
}
else {
while (s.size() - k - st.size() <= s.size() - i - 1)
st.pop_back();
st.push_back(s[i]);
}
}
}
bool flag = 0;
for (int i = 0; i < st.size(); i++) {
if (st[i] != '0') {
flag = true;
}
if (flag) {
cout << st[i];
}
}
if (!flag)
cout << 0;
return 0;
}
思路:
-
定义一个栈用于存储最后会留下的数。
-
根据贪心的思想,位数相同的两个数作比较,只要高位数字越小整个数字就越小,举例199和300.
-
遍历整个字符串,于是对于每个数字可分为可以直接入栈的情况和需要考虑后决定是否入栈的情况:
- 直接入栈的情况:当栈空或者栈顶数字小于等于扫描到的数字,并且栈里数字的个数不能大于原数删除k个数后剩下的数字的个数。
- 需要考虑的情况:
栈顶数字大于扫描到的数字,换而言之,如果我用这个数字替代栈顶,所得的数字可能是更优的。
比如栈里现在的数字是198,扫描到的数字是3,那么这个时候,我用3替换8,可以得到193,甚至可以更往前,得到13. - 那么要如何考虑呢?
首先,根据可以直接入栈的情况,可以很容易得到栈里的数字一定是非递减的。其次当我们找到一个比栈顶小的数字时,我们还应该看看能不能继续往前,找到更适合的位置,比如刚刚举例的198和3.
那么这个查找的过程,我们就可以用二分,用刚刚198和3的例子,找到第一个比3大的数,套二分查找左边界的板子,直接用upper_bound()也行。
找到这个边界之后,就把所有大于等于这个边界的数字全部出栈,在把3入栈,得到13
这个时候还会出现一个问题就是,万一字符串继续往后扫描剩下的数不够了怎么办,这个时候字符串后面剩下几个数字就出栈几个数字,比如3是字符串的倒数第1个数字,那么只能变成193.
-
做完这一切之后,得到的字符串可能以0开头,也可能被删光了,所以要做一些特殊处理,dddd