Deletion problem [greedy]

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 xafter 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
insert image description here
shown in the figure ,
as long as for any a, satisfy a ≤ ba \leq bab will do. In other words,xthe end of a non-decreasing sequence.

The deleted number is after x

Let the number of deletions be a
insert image description here
use the previous result, xand the previous is the end of the non-decreasing sequence. In other words x>y. directly satisfy the deletion xoptimality.

the code

Algorithm process

For better understanding. 1238234657Used 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, 1XXXthe ratio must be 2XXXsmaller.
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 5412you choose 1the longest length 2.
Another restriction is that if you choose 1, the 1previous numbers cannot be selected.
Here I choose the priority queue, that is, the heap as the data carrier. The actual characters are stored
Nodein it , indicating the maximum possible lengthvallen

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

Guess you like

Origin blog.csdn.net/qq_45256489/article/details/122010600