G - Cut and Reorder Editorial by en_translator
It is unnecessary to perform the former operation multiple times; one can assume that the former is performed only once in the beginning, then the latter.
If we consider the process of determining the sequence \((A _ {p _ 1},A _ {p _ 2},\ldots,A _ {p _ N})\) resulting from the former operation from its initial element, we come up with the following DP (Dynamic Programming):
- \(\operatorname{dp}[S]\coloneqq\) the minimum cost required to determine the first \(|S|\) terms so that \(S=\lbrace p _ 1,p _ 2,\ldots,p _ {|S|}\rbrace\).
Here, the “cost” above is defined as the minimum cost that conforming initial \(|S|\) terms contribute; formally:
\[\min _ {\substack{p\text{ is a permutation of }(1,2,\ldots,N)\\\lbrace p _ 1,p _ 2,\ldots,p _ {|S|}\rbrace=S}}\left(C\times\#\lbrace i|1\leq i\lt|S|,p _ i+1\neq p _ {i+1}\rbrace+\sum _ {i=1} ^ {|S|}\left|A _ {p _ i}-B _ i\right|\right).\]
We consider transitions that correspond to a block formed by the former operation.
For each \(S\subset\lbrace1,2,\ldots,N\rbrace\), we perform a transition \(S\rightarrow S\cup\lbrace l,l+1,\ldots,r\rbrace\) for every pair \((l,r)\) such that \(S\cap\lbrack l,r\rbrack=\emptyset\ (1\leq l\leq r\leq N)\).
For each length-\(k\) segment, there are only \(2 ^ {N-k}\) sets \(S\) against which the segment applies a transition; therefore, the total number of transitions is \(\displaystyle\sum _ {k=1} ^ N2 ^ {N-k}(N+1-k)=2 ^ N(N-3)+2=O(2 ^ NN)\).
(Under the assumption that the word size \(w\) satisfies \(N\leq w\),) one can represent a subset of \(\lbrace1,2,\ldots,N\rbrace\) by a single-word integer, so that the overall complexity is \(O(2 ^ NN)\).
The following is sample code.
#include <iostream>
#include <vector>
#include <limits>
#include <bit>
int main() {
using namespace std;
unsigned N;
unsigned long C;
cin >> N >> C;
vector<unsigned long> A(N), B(N);
for (auto &&a : A)
cin >> a;
for (auto &&b : B)
cin >> b;
vector<unsigned long> dp(1U << N, numeric_limits<unsigned long>::max() / 2);
dp[0] = -C; // はじめの 1 ブロックは分割のコストがかからないので -C しておく
const auto chmin{[](auto &&x, const auto &y) {
if (x > y)x = y;
return x;
}};
for (unsigned long S{}; S < 1U << N; ++S)
for (unsigned long B_front(popcount(S)), l{}; l < N; ++l)
for (unsigned long cost{C}, r{l}, i{B_front}; r < N; ++r, ++i) {
if ((S >> r) & 1) // 区間が重なったら終了
break;
cost += max(A[r], B[i]) - min(A[r], B[i]); // 区間のコストに |A[p[i]] - B[i]| を加える
chmin(dp[S | (1UL << r + 1) - (1UL << l)], dp[S] + cost); // コストを更新
}
cout << dp.back() << endl;
return 0;
}
posted:
last update: