TopCoder Open 2012 Round 2A Div1 Medium CucumberWatering
問題
n個のきゅうりに水をやりたい。
きゅうりは一次元の直線上に並んでいて、それぞれの座標はx[i]である。
きゅうりには、与えられた順番で水をやる必要がある。
座標平面上にはk個、テレポーターを自由な場所に設置することができて、
テレポーターを使うことで、テレポーター間を距離0で移動できる。
テレポーターを最適に配置したとき、全てのきゅうりに水をやるのに必要な移動距離の最小値を求めよ。
制約条件
-10^9≦x[i]≦10^9
n, k≦50
方針
動的計画法による。
テレポーターはきゅうりと同じ点に置く場合しか考えなくてよい。
区間ごとに移動をわけて考える。
rec(pos, k)を、posの位置にテレポーターを置き、
あとk回テレポーターを置けるようなときのpos以降の区間における移動距離の最小値とする。
これは、次にテレポーターを置く位置をpos+1からn-1まで試して、再帰することで求まる。
この関数recをメモ化すれば計算量は全体でO(n^2)になる。
ソースコード
以下のコードでは、pos+1からiまでの移動距離を毎回計算しているため計算量はO(n^3)になっている。
int n; vi x; ll dp[51][51]; ll rec(int pos, int k){ if(k < 0) return 1ll << 60; ll &res = dp[pos][k]; if(res >= 0) return res; res = 0; rep(i, n - 1){ int x1 = max(x[pos], x[i]); int x2 = max(x[pos], x[i+1]); res += abs(x2 - x1); } rep(i, n) if(x[i] > x[pos]){ ll tmp = rec(i, k - 1); rep(j, n-1){ int x1 = max(x[pos], min(x[i], x[j])); int x2 = max(x[pos], min(x[i], x[j+1])); tmp += min(abs(x2 - x1), x[i] - abs(x1 - x2) - x[pos]); } res = min(res, tmp); } return res; } class CucumberWatering { public: long long theMin(vector <int> x, int K) { n = x.size(); ::x = x; memset(dp, -1, sizeof(dp)); ll ans = 0; rep(i, n-1) ans += abs(x[i] - x[i+1]); rep(i, n){ ll tmp = rec(i, K - 1); rep(j, n-1){ int x1 = min(x[j], x[i]); int x2 = min(x[j + 1], x[i]); tmp += abs(x1 - x2); } ans = min(ans, tmp); } return ans; } };