Thinking about it:
题目要求是删除D个数,因为总共有N个数,因此也可以看作在N个数中选择d = N - D个数。
怎么选呢?选择第一个数时,肯定是在[ 0, N - D ] 的位置上选,既然选,肯定在这区间内选最大的。如果最大值有多个,那么选位置靠前的,因为这样可以选第一个数的最大值可能用于第二个数。假设,第一个数选的位置为p, 那么第二个数则在[ p+1, N - D + 1 ]的位置选,以此类推,直至全部选完。
因为是在一个区间内选取最大值,所以在选最大值上可以做一些预处理:计算每个位置 i, 离 i 两边最近,且比 i 所在位置值要大的位置。比如 13215,i = 2 时,比 2 大的值,左边最近的在1, 右边最近的在4。知道这两个值,就可以快速判断 2 在区间 [ 1 ,3 ] ,[ 2, 3 ]上是不是最大的了。
Refefence:
1. 《算法竞赛入门经典(第2版)》
2. http://blog.csdn.net/u014800748/article/details/43671343
PS:
这题的解决方法有很多,我在AC后看了他人的题解,每个人的实现方法都有所差异,有些方法值得学习。
Code:
/** * AC @ Sep 8th 2015 * Run Time : 0.049s */ #include <bits/stdc++.h> using namespace std; const int MAXN = 10e5 + 50; int N, D; int l[MAXN], r[MAXN]; // l[i]; the nearest pos bigger than digits[i] on the left // r[i]; the nearest pos bigger than digits[i] on the right string digits; std::vector<int> ans; void init() { int hash[15]; // right for (int i = 0; i < 15; ++i) { hash[i] = MAXN; } for (int i = N-1; i >= 0; --i) { int tmp = MAXN; for (int j = digits[i]- '0' + 1; j < 10; ++j) { tmp = min(hash[j], tmp); } hash[digits[i]- '0'] = i; r[i] = tmp; } memset(hash, -1, sizeof(hash)); // left for (int i = 0; i < N; ++i) { int tmp = -1; for (int j = digits[i]-'0' + 1; j < 10; ++j) { tmp = max(hash[j], tmp); } hash[digits[i]- '0'] = i; l[i] = tmp; } } void find(int start, int d) { if (!d) { return ; } int i; for(i = start; i <= N - d; ++ i) { if (l[i] < start && r[i] > N-d) { break; } } ans.push_back(digits[i]-'0'); find(i+1, d-1); } void work() { init(); ans.clear(); find(0, N-D); for (std::vector<int>::iterator it = ans.begin(); it != ans.end(); ++it) { cout << *it; } cout << endl; } int main(int argc, char const *argv[]) { ios::sync_with_stdio(false); cin.tie(0); while(cin >> N >> D && (N + D)) { cin >> digits; work(); } return 0; }