高级农民
- 积分
- 2716
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2017-6-18
- 最后登录
- 1970-1-1
|
之前描述状态转移方程的时候有点混乱..(但是关于 C(i, j) 和 H(i, j) 的定义应该还是比较明确的)
写了一份代码这样比较好参照:
- #include <algorithm>
- #include <iostream>
- #include <vector>
- constexpr static int kNotSet = -1;
- constexpr static int kInvalid = -2;
- class MaxRepetition;
- // Crushable(i, j) = true 当且仅当 data[i..j] 经过任意变换后可以被完全消除
- class Crushable {
- public:
- explicit Crushable(const size_t length)
- : states_(length, std::vector<int>(length, kNotSet)) {}
- bool Get(int i, int j) {
- if (states_[i][j] == kNotSet) {
- states_[i][j] = Evaluate(i, j);
- }
- return static_cast<bool>(states_[i][j]);
- }
- void SetMaxRepetition(MaxRepetition* max_repetition) {
- max_repetition_ = max_repetition;
- }
- private:
- bool Evaluate(int i, int j);
- MaxRepetition* max_repetition_;
- std::vector<std::vector<int>> states_;
- };
- // (1) 如果 data[i] != data[j],那么 MaxRepetition(i, j) = kInvliad
- // (2) 否则不妨设 data[i] == data[j] == X,那么 MaxRepetition(i, j)
- // 取值为 data[i..j] 经过任意消除过程后可以得到的最长的"全X" 串的长度;
- // 如果不能消除到只剩下X,那么 MaxRepetition(i, j) 取值仍然为kInvalid
- class MaxRepetition {
- public:
- MaxRepetition(const std::vector<int>& data)
- : data_(data),
- states_(data.size(), std::vector<int>(data.size(), kNotSet)) {}
- int Get(int i, int j) {
- if (states_[i][j] == kNotSet) {
- states_[i][j] = Evaluate(i, j);
- }
- return states_[i][j];
- }
- void SetCrushable(Crushable* crushable) {
- crushable_ = crushable;
- }
- private:
- int Evaluate(int i, int j);
- const std::vector<int> &data_;
- Crushable* crushable_;
- std::vector<std::vector<int>> states_;
- };
- ////////////////////////////////////////////////////////////////////////////////
- /////////////////// Crushable 与 MaxRepetition 的状态转移方程 /////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- bool Crushable::Evaluate(int i, int j) {
- // 边界条件:区间长度小于3
- if (i + 1 >= j) {
- return false;
- }
- // 如果存在i < k < j,使得Crushable(i, k)且Crushable(k + 1, j),那么返回true
- for (int k = i + 2; k < j - 2; ++k) {
- if (Get(i, k) && Get(k + 1, j)) {
- return true;
- }
- }
- // 否则考虑是否可以Crush中间若干区间,然后剩下来的同一字符数量大于等于3
- return max_repetition_->Get(i, j) >= 3;
- }
- int MaxRepetition::Evaluate(int i, int j) {
- // 边界条件
- if (data_[i] != data_[j]) {
- return kInvalid;
- }
- // 区间大小为1或者2的情况
- if (i + 1 >= j) {
- return j - i + 1;
- }
- int max_count = kInvalid;
- // Case 1
- if (crushable_->Get(i + 1, j - 1)) {
- max_count = 2;
- }
- // Case 2
- const int target_value = data_[i];
- for (int k = i + 1; k < j; ++k) {
- if (data_[k] != target_value) {
- continue;
- }
- const int left_count = Get(i, k);
- const int right_count = Get(k, j);
- if (left_count != kInvalid && right_count != kInvalid) {
- max_count = std::max(max_count, left_count + right_count - 1);
- }
- }
- return max_count;
- }
- std::vector<int> ComputeShortestRemnant(const std::vector<int>& data) {
- const int length = data.size();
- if (length == 0) {
- return {};
- }
- Crushable crushable(length);
- MaxRepetition max_repetition(data);
- crushable.SetMaxRepetition(&max_repetition);
- max_repetition.SetCrushable(&crushable);
- // 外层的动态规划:找到总长度最大的Crushable区间集合
- std::vector<int> max_crush(length);
- std::vector<int> backtrack(length);
- backtrack[0] = -1;
- for (int j = 2; j < length; ++j) {
- max_crush[j] = max_crush[j - 1];
- backtrack[j] = j - 1;
- for (int i = 0; i < j - 1; ++i) {
- if (crushable.Get(i, j)) {
- const int total_crush = (i == 0 ? 0 : max_crush[i - 1]) + j - i + 1;
- if (total_crush > max_crush[j]) {
- max_crush[j] = total_crush;
- backtrack[j] = i - 1;
- }
- }
- }
- }
- // Backtrack收集无法消除的元素
- std::vector<int> result;
- int curr = length - 1;
- while (curr != -1) {
- const int prev = backtrack[curr];
- if (prev == curr - 1) {
- result.push_back(data[curr]);
- }
- curr = prev;
- }
- std::reverse(result.begin(), result.end());
- return result;
- }
- void PrintVector(const std::vector<int>& values) {
- std::cout << "[";
- bool first = true;
- for (int value : values) {
- std::cout << (first ? "" : " ") << value;
- first = false;
- }
- std::cout << "]\n";
- }
- int main(int argc, char* argv[]) {
- // [1 1]
- PrintVector(ComputeShortestRemnant({1,3,3,3,2,2,2,3,1}));
- // [1 1]
- PrintVector(ComputeShortestRemnant({1,3,3,3,2,2,2,3,1}));
- // [3 1 3 2 3]
- PrintVector(ComputeShortestRemnant(
- {3,1,2,2,2,1,1,1,2,2,3,1,1,2,2,2,1,1,1,2,3}));
- // [2]
- PrintVector(ComputeShortestRemnant({2,3,3,3,2,2,2,3,3}));
- // []
- PrintVector(ComputeShortestRemnant({5,1,1,1,5,1,1,1,5}));
- // []
- PrintVector(ComputeShortestRemnant({1,1,1,2,2,2,1,2,2,2,1}));
- // []
- PrintVector(ComputeShortestRemnant(
- {3,1,1,1,2,1,1,1,2,1,1,1,2,1,1,1,2,2,1,1,
- 1,3,1,1,1,3,2,2,2,3,1,1,1,3,1,1,1,3}));
- return 0;
- }
复制代码
补充内容 (2019-6-16 14:47):
Crushable对应原帖描述的C(i, j),MaxRepetition对应原帖描述的H(i, j),都是用了memo的方法,总时间复杂度为O(n ^ 3) |
|