SRM 217 DIV1 Hard TeleportAndLoader
問題
今位置0に荷物がNつある。
この荷物たちをそれぞれ指定された位置boxesに移動したい。
移動するのに2つの方法がある。
一つ目がテレポート。これは複数の荷物をまとめて、任意の位置へ移動できる。ただし1回に1つの位置にしか移動できない。
1回使うたびに、荷物がどんな数あってもどんな位置でも同じコストteleport_costで移動できる。
2つ目がローダー。これは荷物を一つずつ・左右に距離1ずつ移動できる。一回使うたびにloader_costがかかる。
このとき、最小コストを求めよ
- 1 <= N <= 50
解答
まず、「何個かまとめてテレポート→ローダーで調整」もしくは「テレポーター使わずにローダーだけで移動させる」という操作のみで最小コストにできる。
またテレポートをまとめて行う時、近い位置の荷物たちを移動させるのがよく、つまり位置でソートしたときに区間で表せるまとめかただけを考えれば良い。
テレポートさせる時にどの位置に移動させればよいかだが、これは荷物の位置たちの中央値を取れば良い。
あとはそれをDPでやる
コメント
Σ{|a[i]-x|}の最小化は中央値がいいというのは、恐らく簡単に証明・理解できるのだろうが、自分は"東京大学プログラミングコンテスト2012 L じょうしょうツリー"の解説http://www.utpc.jp/2012/で初めて知った。常識なのかね?
この程度のDPは簡単に素早く解けなければいけないね
コード
#include <vector> #include <algorithm> #include <cstring> #define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i)) #define reu(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i)) #define all(o) (o).begin(), (o).end() #define mset(m,v) memset(m,v,sizeof(m)) #define INF 0x3f3f3f3f using namespace std; typedef vector<int> vi; typedef long long ll; ll dp[55]; struct TeleportAndLoader { vi x; int n; ll tc, lc; ll rec(int i) { if(dp[i] != -1) return dp[i]; ll r = INF; if(i == n) r = 0; else { r = min(r, lc * x[i] + rec(i+1)); rer(t, 1, n-i) { int c = 0; int y = x[i+t/2]; reu(j, i, i+t) c += abs(y - x[j]); r = min(r, tc + lc * c + rec(i + t)); } } return dp[i] = r; } int cheapTransportation(vector <int> boxes, int teleport_cost, int loader_cost) { x = boxes; n = x.size(); sort(all(x)); tc = teleport_cost, lc = loader_cost; mset(dp, -1); return rec(0); } };