topic
给定n位整数d,去掉k(k<n)个数字后剩下的数字按原次序排列组成一个新的正整数。
求最小正整数。
Greedy method generally has two ideas.
Idea 1
One is to reduce the problem size .
For example, in this question, the original question can be changed to
给定n位整数d,去掉1个数字后剩下的数字按原次序排列组成一个新的正整数。
求最小正整数。
Furthermore, it can be transformed into: find a number x
, no matter whether you delete the number before or x
after x, the result will not be better than the result of deleting x .
The following categories are discussed
The deleted number is before x
Let the deleted number be a as
shown in the figure ,
as long as for any a
, satisfy a ≤ ba \leq ba≤b will do. In other words,x
the end of a non-decreasing sequence.
The deleted number is after x
Let the number of deletions be a
use the previous result, x
and the previous is the end of the non-decreasing sequence. In other words x>y
. directly satisfy the deletion x
optimality.
the code
Algorithm process
For better understanding. 1238234657
Used here as a concrete example.
Delete 4 numbers.
The intermediate process is as follows
1238234657
123234657
12234657
1223457
122345
optimization
Each round removes the number at the end of the non-decreasing sequence. Since the end is deleted, there is no need to traverse from the beginning after deletion (it has been judged that it is a non-decreasing sequence), which can reduce the time complexity. But to move elements after deletion, the overall time complexity is still O ( kn ) O(kn)O ( k n ) . Here we choose the linked list as the data carrier to further reduce the time complexity toO ( n ) O(n)O ( n )。
#include "study.h"
int main(int argc, char const *argv[]) {
freopen("7.10.2.txt", "r", stdin);
int k, n;
cin >> k;
string d;
cin >> d;
list<char> li(d.begin(), d.end());
list<char>::iterator it1 = li.begin(), it2 = li.begin();
for (int i = 0; i < k; i++) {
for (++it2; it2 != li.end(); ++it1, ++it2) {
if (*it1 <= *it2) continue;
it2 = it1 = --li.erase(it1);
break;
}
};
for (it1 = li.begin(); it1 != li.end(); ++it1) cout << *it1;
cout << '\n';
return 0;
}
Idea 2
The other is to decompose the steps of the problem.
In this question, the original question can be changed to
从 整数d 中挑选数字,依次填入长度为 n-k 的数组中。
求数组组成的最小数字
This idea is simpler
because the high digits are filled first and the number of digits is determined, as long as you fill in as small a number as possible,
for example, 1XXX
the ratio must be 2XXX
smaller.
But there is a limitation that you cannot choose the number that is too late, otherwise the number of digits is not enough.
For example, if 5412
you choose 1
the longest length 2
.
Another restriction is that if you choose 1
, the 1
previous numbers cannot be selected.
Here I choose the priority queue, that is, the heap as the data carrier. The actual characters are stored
Node
in it , indicating the maximum possible lengthval
len
the code
#include "study.h"
// 删数问题
struct Node {
char val;
int len;
bool operator<(const Node &r) const {
if (val == r.val) {
// 长度优先
return len < r.len;
}
// val 小优先
return r.val < val;
}
};
int main(int argc, char const *argv[]) {
freopen("7.10.2.txt", "r", stdin);
string d;
int k, n;
cin >> k >> d;
n = d.size();
priority_queue<Node> pq, pq2;
for (int i = 0; i < n; i++) pq.push({
d[i], n - i});
// need需要的数字个数,lastlen上一次选择的长度
for (int need = n - k, lastlen = n; !pq.empty() && need;) {
Node cur = pq.top();
pq.pop();
// 这次选择的长度不能比上一次的小
if (cur.len > lastlen) continue;
if (need > cur.len) {
// 长度不够,放到下一轮
pq2.push(cur);
} else {
// 存入答案,并更新需要的数字个数,和选择的长度
d[n - k - (need--)] = cur.val;
lastlen = cur.len;
while (!pq2.empty()) {
// 把没选的加回来
pq.push(pq2.top());
pq2.pop();
}
}
}
d[n - k] = '\0';
cout << d.c_str() << '\n';
return 0;
}
appendix
study.h header file
#ifndef STUDY
#define STUDY
#include <bits/stdc++.h>
using namespace std;
#define MAX_INT 0x3f3f3f3f
#define MIN_INT 0xc0c0c0c1
istream &operator>>(istream &in, vector<int> &v) {
v.clear();
int x;
if (in >> x) {
v.push_back(x);
string s;
getline(in, s);
stringstream ss(s);
while (ss >> x) v.push_back(x);
}
return in;
}
istream &operator>>(istream &in, vector<vector<int>> &v) {
for (int i = 0; i < v.size(); i++) cin >> v[i];
return in;
}
ostream &operator<<(ostream &out, vector<int> arr) {
for (int i = 0; i < arr.size(); i++) out << arr[i] << ' ';
return out;
}
ostream &operator<<(ostream &out, vector<vector<int>> arr) {
for (int i = 0; i < arr.size(); i++) out << arr[i] << '\n';
return out;
}
#endif