Problem p02315 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <algorithm>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <utility>
#include <vector>

using ll = long long;
class KnapsackZDD {
  const int n;
  const int W;
  const int *w;
  const ll *v;
  using P = std::pair<int, int>;
  struct Node {
    P child[2];
    Node() { child[0] = child[1] = P(0, 0); }
  };
  std::vector<std::unordered_map<int, Node>> g;
  std::vector<std::unordered_map<int, ll>> value;

public:
  KnapsackZDD(int n, int W, int *w, ll *v) : n(n), W(W), w(w), v(v) {}

  int getRoot(int &state) const {
    state = 0;
    return n;
  }

  // n + 0 : 1-end
  // n + 1 : 0-end
  int getChild(int &state, int level, int value) const {
    if (value == 1)
      state += w[level];
    ++level;
    if (state > W)
      return n + 1; // infeasible
    return level;
  }

  void construct() {
    int vRoot = 0;
    g.resize(getRoot(vRoot) + 1);
    g[0][vRoot] = Node();
    g[n][0] = g[n][1] = Node();

    for (int i = 0; i < n; ++i) {
      for (auto &pair : g[i]) {
        auto &node = pair.second;
        int state = pair.first;
        for (int x = 0; x < 2; ++x) {
          int nextLevel = getChild(state, i, x);
          if (nextLevel >= n) {
            node.child[x] = P(n, n + 1 - nextLevel);
          } else {
            if (!g[i + 1].count(state)) {
              g[i + 1][state] = Node();
            }
            node.child[x] = P(i + 1, state);
          }
        }
      }
    }
  }

  ll evaluate() {
    value.resize(n + 1);
    return evalNode(0, 0);
  }
  ll evalNode(const P &pair) { return evalNode(pair.first, pair.second); }
  ll evalNode(const int &level, const int &state) {
    { // check memo
      auto it = value[level].find(state);
      if (it != value[level].end())
        return it->second;
    }
    auto &res = value[level][state];
    if (level == n)
      return res = state ? 0 : (1 << 31);
    auto &&node = g[level][state];
    return res = std::max(evalNode(node.child[0]),
                          evalNode(node.child[1]) + v[level]);
  }
};

constexpr int MAX_N = 102;
int w[MAX_N];
ll v[MAX_N];
int main() {
  int n, W;
  auto input = [&](std::istream &ifs) {
    ifs >> n >> W;
    for (int id = 0; id < n; ++id) {
      ifs >> v[id] >> w[id];
    }
  };
  // std::ifstream ifs("in.txt");
  // input(ifs);
  input(std::cin);

  KnapsackZDD knapsack(n, W, w, v);
  knapsack.construct();

  std::cout << knapsack.evaluate() << std::endl;
  return 0;
}


Error: TLE

Fixed submission:

#include <algorithm>
#include <iostream>
#include <stack>
#include <vector>

using TYPE = long long;

class KnapsackProblem {
private:
  size_t n;            // アイテムの個数
  TYPE W;              // ナップサックに入れられる最大重量
  std::vector<TYPE> v; // v[i] := アイテムiの価値
  std::vector<TYPE> w; // w[i] := アイテムiの重さ
public:
  struct Result {
    double value;               // 価値合計
    std::vector<int> selection; // 変数の選択
    bool isInteger;             // 整数制約を満たしているか
  };
  KnapsackProblem(size_t n, TYPE W, std::vector<TYPE> v, std::vector<TYPE> w)
      : n(n), W(W), v(v), w(w) {}
  Result calcSubProblem(std::vector<int> x) const {
    double res = 0; // 価値合計
    bool isInteger = true;

    TYPE restW = W;
    struct Item {
      TYPE v, w;
      size_t i;
    }; // (価値v, 重さw, アイテム番号i)
    std::vector<Item> item;
    for (size_t itemIdx = 0; itemIdx < n; ++itemIdx) {
      if (x[itemIdx] == -1) { // x[itemIdx]がまだ決められていないとき
        item.emplace_back(Item{v[itemIdx], w[itemIdx], itemIdx});
      } else { // x[itemIdx]の値が決まっているとき
        restW -= w[itemIdx] * x[itemIdx];
        res += v[itemIdx] * x[itemIdx];

        if (restW < 0) { // 与えられたxでは実行可能解が存在しないとき
          return Result{-1.0, x, false};
        }
      }
    }

    // v/wの効率で降順ソート(greedy)
    std::sort(std::begin(item), std::end(item), [&](Item &lhs, Item &rhs) {
      return lhs.v * rhs.w > rhs.v * lhs.w;
    });

    // 未確定の変数をまだ使っていないとする
    for (size_t itemIdx = 0; itemIdx < n; ++itemIdx) {
      if (x[itemIdx] == -1)
        x[itemIdx] = 0;
    }

    for (auto &&it : item) { // 次に効率が最もよいアイテムitについて
      if (restW == 0)
        break;                 // ちょうど入れられなければ終了
      if (restW - it.w >= 0) { // itを完全に入れられるとき
        res += it.v;
        restW -= it.w;
        x[it.i] = 1;
      } else { // itを完全には入れられないとき
        res += (double)it.v * restW / it.w;
        // x[it.i] = -1; // it.i番目のアイテムを実数値にした
        x[item[0].i] = -1; // 最も効率の良いアイテムを実数値にした
        isInteger = false;
        break;
      }
    }
    return Result{res, x, isInteger};
  }

  size_t getN() const { return n; }
};

class BranchAndBound {
private:
  class Node {
  private:
    const TYPE value;
    const std::vector<int> valueSelection;
    const std::vector<int> fixedSelection; // -1 -> not selected
  public:
    static BranchAndBound *BB;
    Node() : value(-1), valueSelection(), fixedSelection() {}
    Node(TYPE value, std::vector<int> valueSelection,
         std::vector<int> fixedSelection)
        : value(value), valueSelection(valueSelection),
          fixedSelection(fixedSelection) {}

    // 変数の選択情報から新しいノードを作成する
    static Node makeNode(const std::vector<int> &selection) {
      auto &&result = BB->problem.calcSubProblem(selection);
      return makeNode(result, selection);
    }
    static Node makeNode(const KnapsackProblem::Result &result,
                         const std::vector<int> &selection) {
      return Node(result.value, result.selection, selection);
    }

    TYPE getValue() const { return value; }
    std::vector<int> getValueSelection() const { return valueSelection; }
    std::vector<int> getFixedSelection() const { return fixedSelection; }
  };

  KnapsackProblem problem;
  KnapsackProblem::Result best;
  size_t nodeNum;
  std::stack<Node> notSerchNodes;

public:
  BranchAndBound(KnapsackProblem problem)
      : problem(problem), best({-1}), nodeNum(0) {
    Node::BB = this;
  }

  // 01ナップサック問題を分枝限定法で解く
  KnapsackProblem::Result solve() {
    // step 0. (初期化)
    std::vector<int> emptySet(problem.getN(), -1);

    KnapsackProblem::Result result(problem.calcSubProblem(emptySet));
    if (result.isInteger) { // 連続問題を解いてそれが整数制約を満たしていれば
      return result; // 終了
    } else {
      notSerchNodes.push(Node::makeNode(result, emptySet));
    }

    // step 1. (分枝ノードの選択)
    while (!notSerchNodes.empty()) {
      Node selectedNode = notSerchNodes.top();
      notSerchNodes.pop();
      if (selectedNode.getValue() <
          best.value) { // step 4.の(ノード検査)をここに入れ込む
        continue;       // 限定操作
      }

      // step 2. (分枝変数の選択)
      int boundIdx = -1;
      std::vector<int> selection = selectedNode.getValueSelection();
      for (size_t variableIdx = 0; variableIdx < selection.size();
           ++variableIdx) {
        if (selection[variableIdx] == -1) { // 連続値をとる変数を見つけたら
          boundIdx = variableIdx; // その変数を分枝変数とする
          break;
        }
      }

      // 子問題の変数選択
      std::vector<std::vector<int>> subSelection(
          2, selectedNode.getFixedSelection());

      subSelection[0][boundIdx] = 0;
      subSelection[1][boundIdx] = 1;

      // step 3. (分枝計算)
      std::vector<KnapsackProblem::Result> subResult(2);
      for (size_t childIdx = 0; childIdx < 2; ++childIdx) {
        // 子問題を解く
        subResult[childIdx] = problem.calcSubProblem(subSelection[childIdx]);
      }

      // step 4. (ノード検査)
      for (size_t childIdx = 0; childIdx < 2; ++childIdx) {
        auto &&res = subResult[childIdx];
        if (res.value == -1) { // 子ノードが解けなかったとき
          continue;
        }
        if (res.value > best.value) { // 子ノードが暫定解よりよいとき
          if (res.isInteger) {        // 整数制約を満たしているとき
            best = res;               // 暫定解を更新
          } else { // 暫定解よりはよいが整数制約を満たしていないとき
            notSerchNodes.push(
                Node::makeNode(res, subSelection[childIdx])); // 分枝操作
          }
        }
      }
    }
    return best;
  }
};
BranchAndBound *BranchAndBound::Node::BB;

// 変数の設定(直接指定する)
// void input(size_t& n, TYPE& W, std::vector<TYPE>& v, std::vector<TYPE>& w) {
//	n = 6;
//	W = 15;
//	v = std::vector<TYPE>({15,3,1,8,2,4});
//	w = std::vector<TYPE>({11,2,3,5,1,4});
//}

// 変数の設定(標準入力から受け取る)
void input(size_t &n, TYPE &W, std::vector<TYPE> &v, std::vector<TYPE> &w) {
  std::cin >> n >> W;
  v.resize(n);
  w.resize(n);
  for (size_t itemIdx = 0; itemIdx < n; ++itemIdx) {
    std::cin >> v[itemIdx] >> w[itemIdx];
  }
}

// 結果の出力(標準出力へ)
void output(const KnapsackProblem::Result &result, const std::vector<TYPE> &v) {
  TYPE valueSum = 0;
  for (size_t itemIdx = 0; itemIdx < result.selection.size(); ++itemIdx) {
    if (result.selection[itemIdx] == 1) {
      valueSum += v[itemIdx];
    }
  }
  std::cout << valueSum << std::endl;

  /*std::cerr << "Value  : " << valueSum << std::endl;
  std::cerr << "Select :";
  for (auto& item : result.selection) {
          std::cerr << " " << item;
  }
  std::cerr << std::endl;*/
}

int main() {

  size_t n;
  TYPE W;
  std::vector<TYPE> v, w;

  input(n, W, v, w);

  KnapsackProblem problem(n, W, v, w);
  BranchAndBound BB(problem);

  KnapsackProblem::Result result(BB.solve());

  output(result, v);

  return 0;
}