削除問題[貪欲]

トピック

给定n位整数d,去掉k(k<n)个数字后剩下的数字按原次序排列组成一个新的正整数。
求最小正整数。

貪欲な方法には、一般に 2 つの考え方があります。

アイデア 1

1 つは、問題のサイズを小さくすることです。
たとえば、この質問では、元の質問を次のように変更できます。

给定n位整数d,去掉1个数字后剩下的数字按原次序排列组成一个新的正整数。
求最小正整数。

さらに、これは次のように変換できます。find a number x、 x の前後のどちらの数字を削除してもx、結果は x を削除した結果よりも良くなりません
次のカテゴリについて説明します

削除された番号は x より前にあります

削除する数を
ここに画像の説明を挿入
図に示すように a とします。
ただし、 a ≤ ba \leq baを満たす限り、あるbはやります。言い換えれば、x減少しないシーケンスの終わりです。

削除された番号は x の後にあります

削除の数は
ここに画像の説明を挿入
前の結果を使用し、x前の結果は非減少シーケンスの終わりであるとします。つまりx>y削除のx最適性を直接満たします。

コード

アルゴリズム処理

より良い理解のために。ここでは具体的な例として1238234657使用します。
4つの数字を削除します。
中間処理は以下の通り

1238234657
123234657
12234657
1223457
122345

最適化

各ラウンドでは、減少しないシーケンスの最後の番号が削除されます。末尾が削除されるため、削除後に先頭からたどる必要がなくなり(非減少系列と判断される)、時間計算量を軽減できる。ただし、削除後に要素を移動する場合、全体的な時間計算量は依然としてO ( kn ) O(kn)です。O ( k n )ここでは、時間計算量をさらにO ( 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;
}

アイデア 2

もう 1 つは、問題のステップを分解することです。
この質問では、元の質問を次のように変更できます。

从 整数d 中挑选数字,依次填入长度为 n-k 的数组中。
求数组组成的最小数字

この考え方は簡単です。
なぜなら、上位の桁が最初に埋められ、桁数が決定されるからです。ただし、入力する数値ができるだけ小さい限り、たとえば、
比率1XXX2XXX小さくなければなりません。
ただし、桁数が足りないと遅すぎる番号を選択できないという制限があります。
たとえば、最長の長さを5412選択したとしますもう 1 つの制限は、 を選択した場合前の番号は選択できないことです。ここでは優先キュー、つまりデータキャリアとしてのヒープを選択します。実際の文字はそこに保存され、可能な最大長を示します12
11

Nodevallen

コード

#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;
}

付録

Study.h ヘッダー ファイル

#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

おすすめ

転載: blog.csdn.net/qq_45256489/article/details/122010600
おすすめ