M04137:最小新整数
monotonous-stack, http://cs101.openjudge.cn/practice/04137/
思路:
- 法一(考场 ver.):注意到
最多只有 位,因此可以直接枚举出所有可能的删除方案,再取最小值即可。时间复杂度视实现可能为 或 ,其中 。 - 法二:考虑 dp,设
表示在前 位中删去 位所能得到的最小数,答案即为 。时间复杂度为 。 - 法三:考虑贪心,令
,则我们需要给新数依次确定尽可能小的第 位,其中 。 - 设新数的第
位为原数的第 位,原数从高到低的第 位为 ,则对于 ,我们有如下限制: - (1)
,左边确保了新数的前 位可以通过删除原数得到,右边确保了第 位还有的选。 - (2)
尽可能小。那要是有多个,我们应该怎么办呢? - (3) 在满足 (2) 的前提下,
尽可能小,这样的话可以给后面留下更大的选择余地。 - 类似滑动窗口地单调队列即可。时间复杂度为
。 那么问题来了,为什么不开到 卡掉上面两种做法?
代码(法三):
cpp
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int digit[10];
deque<int> q;
int main(){
int t;
cin >> t;
for (int i = 1; i <= t; i++){
int n, k, k_, len = 0;
cin >> n >> k;
while (n > 0){
digit[++len] = n % 10;
n /= 10;
}
reverse(digit + 1, digit + len + 1);
k_ = len - k;
q.clear();
for (int j = 1; j <= k; j++){
while (!q.empty() && digit[q.back()] > digit[j]) q.pop_back();
q.push_back(j);
}
for (int j = 1; j <= k_; j++){
int add = j + k, cur;
while (!q.empty() && digit[q.back()] > digit[add]) q.pop_back();
q.push_back(add);
cur = q.front();
q.pop_front();
cout << digit[cur];
}
cout << endl;
}
return 0;
}