公式

M - 点の距離/Distance on the Line 解説 by Nyaan


この問題は次のような問題として考えることができます。

  • はじめ変数 \(x=0\) がある。\(i=1,\dots,N\) について \(x\)\(A_i\)\(-A_i\) のいずれか一方を足す。最終的に得られる \(x\) のうち \(\mathrm{abs}(x)\) の最小値は?

上記のように言い換えると、この問題は部分和問題と等価な問題で、動的計画法 (DP) を利用して \(\mathrm{O}(N^2 \max(A))\) で計算できると分かります。
このままでは実行時間制限に間に合いませんが、DP で持つ値が true/false の 2 値なので DP の値を 64 個置きに区切って 64 bit 整数に格納していくことで DP の 64 個の遷移を 1 回でまとめて行う定数倍高速化が可能で、計算量は \(w=64\) として \(\mathrm{O}(N^2 \max(A) / w)\) に高速化できて実行時間制限に十分間に合います。実装は C++ を利用すれば std::bitset 等を利用することで容易に実装できます。

  • 実装例 (C++)
#include <bitset>
#include <iostream>
#include <map>
#include <vector>
using namespace std;

bitset<4006400> bs;

int solve(int, vector<int> D) {
  bs.reset();
  int offset = 2002002;
  bs[offset] = 1;
  for (auto& d : D) bs = (bs << d) | (bs >> d);
  int ans = 1 << 30;
  for (int i = 0; i < (int)bs.size(); i++) {
    if (bs.test(i)) ans = min(ans, abs(i - offset));
  }
  return ans;
}

int main() {
  int N;
  cin >> N;
  vector<int> D(N - 1);
  for (auto& d : D) cin >> d;
  cout << solve(N, D) << "\n";
}

投稿日時:
最終更新: