Official
D - プリンターの割り当て / Printer Assignment Editorial
by
D - プリンターの割り当て / Printer Assignment Editorial
by
kyopro_friends
この問題は答えを二分探索することで解くことができます。
「全ての依頼を \(X\) 分以内に処理できるか?」という判定問題を考えます。
このとき「処理できる最大ページ数の小さなプリンターから順に、割り当て可能な依頼を割り当てられるだけ割り当てる」とするのが最適になります。
証明
そのような割り当てを $X$ とし、それ以外の最適な割り当てを任意に 1 つとり $Y$ とします。$X$ と $Y$ で割り当てられた依頼が異なっている最小のプリンターを $i$ とします。$X$ はその割り当て方から、さらに多くの依頼を $X$ に割り当てることはできないので、プリンター $i$ への割り当てが異なっているということは「$X$ ではプリンター $i$ に割り当てられているが、$Y$ では別のプリンターに割り当てられている」という依頼 $j$ が存在します。割り当て $Y$ をベースに、依頼 $j$ をプリンター $i$ に割り当て直すことで新たな割り当てを作ることを考えます。プリンター $i$ に余裕があるならそのまま割り当てればよいです。余裕がない場合、$X$ の割り当て方から、「$X$ ではプリンター $i$ に割り当てられていないが、$Y$ ではプリンター $i$ に割り当てられている」という依頼が存在します。依頼 $j$ をこの依頼と入れ替えます。 以上の操作により、損をすることなく、$X$ との食い違いの個数が $1$ つ以上少ない新たな割り当てを作ることができるため、これを繰り返すことで割り当て $X$ が最適解の1つを与えることがわかります。よって、予めプリンターを最大ページ数順、依頼をページ数順に並べておくことで、\(O(N+M)\) でこの判定問題を解くことができるので、答えを二分探索することにより、全体で \(O((N+M)(\log \max T_i+\log N+\log M))\) でこの問題を解くことができます。
実装例 (C++)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, m;
cin >> n >> m;
vector<pair<int, int>> wt(n);
for(int i=0; i<n; i++) cin >> wt[i].first >> wt[i].second;
vector<int> p(m);
for(int i=0; i<m; i++) cin >> p[i];
sort(wt.begin(), wt.end());
sort(p.begin(), p.end());
if(p.back() > wt.back().first){
// どのプリンターでも処理できない依頼が存在する
cout << -1 << endl;
return 0;
}
auto f=[&](long long x){
// 時間 x 以内に全ての依頼を処理できるか?
int i = 0;
for(auto[w, t]: wt){
for(int j=0; j<x/t; j++){
if(i < m && p[i] <= w){
i++;
}else{
break;
}
}
}
return i == m;
};
long long ng = 0, ok = 1e15;
while(ok - ng > 1){
long long m = (ok + ng) / 2;
if(f(m)){
ok = m;
}else{
ng = m;
}
}
cout << ok << endl;
}
実装例 (Python)
N, M = map(int, input().split())
WT = [tuple(map(int, input().split())) for _ in range(N)]
WT.sort()
P = [int(input()) for _ in range(M)]
P.sort()
if P[-1] > WT[-1][0]:
# どのプリンターでも処理できない依頼が存在する
print(-1)
exit()
def f(x):
# 時間 x 以内に全ての依頼を処理できるか?
i = 0
for W, T in WT:
for _ in range(x//T):
if i < M and P[i] <= W:
i += 1
else:
break
return i == M
ng = 0
ok = 10**15
while ok - ng > 1:
m = (ok+ng) // 2
if f(m):
ok = m
else:
ng = m
print(ok)
posted:
last update:
