ログインしてください。
公式
D - ロープの分割 / Splitting a Rope 解説
by
D - ロープの分割 / Splitting a Rope 解説
by
kyopro_friends
この問題は答えを二分探索することで解くことができます。
以下では、入力で与えられた \(K\) から \(1\) を引き、切る回数を \(K\) とします。
まずは次の判定問題を考えます。
問題1: \(K\) か所を選んで切断することで、長さの最大値を \(X\) 以下にできるか判定せよ。
この問題は次の問題と同値です。
問題2: 長さの最大値が \(X\) 以下になるように切断するとき、切断回数を \(K\) 回以下にできるか判定せよ。
少ない回数の切断で長さの最大値を \(X\) 以下にするためには「丸太を端から見て \(X\) を超える直前の箇所で切る」を繰り返すのが最適です。
証明
「丸太を端から見て $X$ を超える直前の箇所で切る」という方法を切り方 $P$ と呼び、切る箇所を $P_1,\dots,P_k$ とします。長さの最大値が $X$ 以下になる切り方のうち切断回数が最小のものを任意に1つ選び、切り方 $Q$ と呼び、この方法で切る箇所を $Q_1,\dots,Q_{k'}$ とします。最小性から $k\geq k'$ です。$P,Q$ を先頭から比較します。- 全ての $i=1,\dots,k'$ で$P_i=Q_i$ のとき 切り方 $Q$ より $Q_{k'}$ から端までの長さは $X$ 以下なので、切り方 $P$ の定義から、切り方 $P$ においてもこの間で切ることはありません。よって $P=Q$ となります。
- ある $i$ が存在して $P_i \neq Q_i$ となるとき そのような最小の $i$ を取ると、切り方 $P$ の定義から、 $Q_i$ は $P_i$ より "手前" にあります。このとき、 $Q_i$ を $P_i$ に置き換えた切り方 $(Q_1,\dots,Q_{i-1},P_i,Q_{i+1},\dots)$ も、長さの最大値は $X$ 以下です。(切り方 $Q$ と長さが変わっている $2$ 箇所について、$Q_{i-1},P_i$ 間は $P_{i-1},P_{i}$ 間に等しく $X$ 以下であり、$P_i, Q_{i+1}$ 間は $Q_i,Q_{i+1}$ 間未満なので $X$ 以下です)。 よってこれを繰り返すことで $Q$ を $P$ に一致させることができ、 $P$ の切断回数が最小であることがわかりました。
よって、この判定問題は \(O(N)\) で解けることがわかりました。
元の問題は答えを二分探索することで、この判定問題に帰着できます。よって、\(O(N\log \sum A_i)\) で解くことができます。
判定問題を解く際、実装によっては \(X < \max A_i\) のケースに注意してください。
実装例 (C++)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, k;
cin >> n >> k;
k--;
vector<int>a(n);
for(int i=0; i<n; i++) cin >> a[i];
auto solve=[&](long long m){
int cut = 0;
long long crr = 0;
for(int i=0; i<n; i++){
if(crr + a[i] > m){
cut++;
crr = 0;
}
crr += a[i];
}
return cut <= k;
};
long long ng = *max_element(a.begin(), a.end()) - 1;
long long ok = 1e15;
while(ok - ng > 1){
long long m = (ok + ng) / 2;
if(solve(m)){
ok = m;
}else{
ng = m;
}
}
cout << ok << endl;
}
実装例 (Python)
N, K = map(int, input().split())
A = list(map(int, input().split()))
K -= 1
def solve(m):
cut = 0
crr = 0
for a in A:
if crr + a > m:
cut += 1
crr = 0
crr += a
return cut <= K
ng = max(A) - 1
ok = 10**15
while ok - ng > 1:
m = (ok + ng) // 2
if solve(m):
ok = m
else:
ng = m
print(ok)
投稿日時:
最終更新:
