TopCoder SRM614 解説
description
Transcript of TopCoder SRM614 解説
SRM614 解説(自担当分)Div2 Medium/Hard
Div1Easy/Hard
@EmKjp
Div2 Medium : MinimumSquareEasy
問題• N 個の二次元座標が与えられる ( 3 N 50 )≦ ≦• このうち少なくとも N-2 個の点を
内側 ( 境界上は NG) に含むような正方形の最小面積を求めよ
• ただし、「正方形の辺は垂直または水平」「正方形の頂点は整数座標」でないといけない
Div2 Medium : MinimumSquareEasy
解法• 「 N 個の点 から N-2 個選ぶ」ことと
「 N 個の点から除外する2個の点を選ぶ」 ことは同じ• 除外する2個の点の選び方は N * (N – 1) / 2 通り• N 50 ≦ のため、高々 1225 通り
Div2 Medium : MinimumSquareEasy
解法• N-2 個の点集合全パターンについて、
N-2 個の点全てを内側に含むような正方形の最小面積を出す• バウンディングボックスの辺のうち、長いほうを求める
• 総計算量: O(N^3)
Div2 Medium : MinimumSquareEasy解答例( C#)
public long minArea(int[] x, int[] y) {
int n = x.Length;
long minSide = long.MaxValue;
for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) {
var xs = new List<int>();
var ys = new List<int>();
for (int k = 0; k < n; k++) {
if (i == k || j == k) continue;
xs.Add(x[k]);
ys.Add(y[k]);
}
var side = Math.Max(xs.Max() - xs.Min(), ys.Max() - ys.Min()) + 2;
minSide = Math.Min(minSide, side);
} return minSide * minSide;
}
Div1 Easy : MinimumSquare
問題• N 個の二次元座標が与えられる ( 2 N 100 )≦ ≦• このうち少なくとも K 個の点を ( 1 K N )≦ ≦
内側 ( 境界上は NG) に含むような正方形の最小面積を求めよ
• ただし、「正方形の辺は垂直または水平」「正方形の頂点は整数座標」でないといけない
Div1 Easy : MinimumSquare• 辺上が NG だとやや扱いづらいので
辺上も OK にした上であとで 辺の長さ+2 にする
Div1 Easy : MinimumSquare• 正方形の左上座標の候補は
(X[p], Y[q]) の N^2 通り (0 p N-1, 0 q N-1)≦ ≦ ≦ ≦
Div1 Easy : MinimumSquare• 左上座標の候補についてそこからどれぐらい辺を伸ばせ
ば、K 個以上の点を含めることができるかを探索すればよい
Div1 Easy : MinimumSquare
辺の長さの探索方法はいろいろ• 辺の長さで二分探索
→ 総計算量: O(N^3 log( 辺の最大の長さ ))• 辺の長さの候補は実は N 個なのでそれらを全て調べる
→ 総計算量: O(N^4) • N 個の辺の長さの候補に対して二分探索• → 総計算量: O(N^3 log(N))
Div1 Easy : MinimumSquare
オーバーフローに注意!• (left <= x[i] <= left + size) のような内外判定は
符号付き 32bit 整数だとオーバーフローします• -100,000,000 <= left <= 100,000,000• 0 <= size <= 200,000,000• left + size <= 300,000,000 > 2^31
• 64bit 整数型を使いましょう。
Div1 Easy : MinimumSquare解答例( C#) O(N^4)
public long minArea(int[] x, int[] y, int K) {
int N = x.Length;
long minSide = long.MaxValue;
foreach (var leftX in x) foreach (var leftY in y)
for (int k = 0; k < N; k++) {
long side = Math.Max(Math.Abs(leftX - x[k]), Math.Abs(leftY - y[k]));
int contain = 0;
for (int p = 0; p < N; p++) {
if (leftX <= x[p] && x[p] <= leftX + side && leftY <= y[p] && y[p] <= leftY + side)
contain++;
}
if (contain >= K) minSide = Math.Min(minSide, side);
}
return (minSide + 2) * (minSide + 2);
}
Div2 Hard : TorusSailingEasy
問題• 格子状に N × M のエリアがある。• シエルは (0,0) からスタートする• シエルは、もし (x, y) にいる場合は
• ((x + 1 + N) % N, (y +1 + M) % M) • ((x – 1 + N) % N, (y - 1 + M) % M)
のどちらに等確率で移動する• (goalX, goalY) に到達するまでの移動回数の期待値を求め
よ• 到達できない場合は -1 を返せ
Div2 Hard : TorusSailingEasy
制約• 2 N 10≦ ≦• 2 M 10≦ ≦• 0 goalX N-1≦ ≦• 0 goalY M-1≦ ≦• (goalX, goalY) != (0, 0)
Div2 Hard : TorusSailingEasy
( a % N, a % M) = (goalX, goalY) であるような最小の自然数 a
(-b % N, -b % N) = (goalX, goalY) であるような最小の自然数 b を使って、「 0 から +1 または -1 にランダム・ウォークしたとき、 a または – b に到達するまでの移動回数の期待値を求めよ」 と言い換えられる
ab
Div2 Hard : TorusSailingEasy
a, b の求め方いろいろ• 0 から M*N-1 までの整数を全探索する
• 計算量: O ( MN)
• (p % N == goalX) な整数 p だけを探索する• 例:
for (int p = goalX; p < N * M; p += N) if (p % M == goalY) return p;• 計算量 : O(M)
• 中国剰余定理で連立合同式を解く• 計算量: O(log(N)) or O(log(M))
N,M 10 ≦ のため、どれでも十分間に合う
Div2 Hard : TorusSailingEasy
期待値の求め方(連立方程式を解く方法)E[x] = x からゴールへ到達する移動回数の期待値とすると E[a] = 0 , E[-b] = 0 , E[i] = (E[i+1] + E[i-1]) / 2 + 1 (-b < i < a)
となり、 (a + b + 1) 変数連立方程式を導出できる
N 変数連立方程式はガウスジョルダン法やガウスの消去法 などを使うと O ( N^3) で解けるため、計算量は O((MN)^3)となる。
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法を反復させる方法)DP[step 数 ][ 位置 ] =その step 数でその位置に到達する確率として、 ゴール以外の位置 pos についてDP[step+1][pos+1] += DP[step][pos] / 2
DP[step+1][pos -1] += DP[step][pos] / 2
を 時間ギリギリまで多くの step 数まで計算すると∑ step * (DP[step][a] + DP[step][-b]) が答えになる。
• 数十万回移動してもゴールにたどり着かない確率は非常に低いため、ある程度の移動回数以降は無視できる。
• 計算量: O (反復回数 *MN)
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法)
• グラフは左右対称• 各地点からゴールまでの期待値も左右対称• ということは対称な点同士を統合できる!
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法)グラフを変換する
折り返す 対称点を統合
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法)変換後 • (a + b) が偶数の場合
• (a + b) が奇数の場合
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法) (a + b) が偶数の場合
• ゴールから一番遠い点から 0, 1, 2 … とする• E[p] = p から (p+1) までの移動回数の期待値 とする• E[0] = 1 ( 必ず次は “ 1” に移動する )• E[1] = (E[0] + E[1]) / 2 + 1 → E[1] = 3• E[2] = (E[1] + E[2]) / 2 + 1 → E[2] = 5• E[n] = 2 * n + 1
G 5 3 2 14 0
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法) (a + b) が偶数の場合• p から (p+1) までの移動回数の期待値 E[p] = 2 * p + 1• 0 から p までの移動回数の期待値= E[0] + E[1] + … + E[p-1]= 1 + 3 + 5 + … + 2 * (p-1) +1 = p^2
• x から y までの移動回数の期待値 Eeven [x,y] (x < y)= E[x] + E[x+1] … + E[y-1]= (E[0] + E[1] + .. + E[y-1]) – (E[0] + E[1] + … E[x-1])= y^2 – x^2 = (y + x)(y – x)
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法) (a + b) が偶数の場合 - まとめ• 変形後のゴール位置 G = (a + b) / 2• 変形後のスタート位置 S = G – min(a, b)• S から G までの移動回数の期待値Eeven [S,G] = (G + S) (G – S)
• 計算量: O(1)
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法) (a + b) が奇数の場合
• E[p] = p から (p+1) までの移動回数の期待値 とする• E[0] = E[0] / 2 + 1 → E[0] = 2• E[1] = (E[0] + E[1]) / 2 + 1 → E[1] = 4• E[2] = (E[1] + E[2]) / 2 + 1 → E[2] = 6• E[n] = 2 * n
G 5 3 2 14 0
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法) (a + b) が奇数の場合• p から (p+1) までの移動回数の期待値 E[p] = 2 * p• 0 から p までの移動回数の期待値= E[0] + E[1] + … + E[p-1]= 2 + 4 + 6 + … + 2 * (p-1) = p * (p + 1)
• x から y までの移動回数の期待値 Eodd[x,y] (x < y)= E[x] + E[x+1] … + E[y-1]= (E[0] + E[1] + .. + E[y-1]) – (E[0] + E[1] + … E[x-1])= y*(y+1) – x*(x+1) = y^2-x^2+y-x=(y+x)(y-x)+(y-x) = (y+x+1)(y-x)
Div2 Hard : TorusSailingEasy
期待値の求め方(動的計画法) (a + b) が奇数の場合 - まとめ• 変形後のゴール位置 G = (a + b - 1) / 2• 変形後のスタート位置 S = G – min(a, b)• S から G までの移動回数の期待値Eodd[S,G] = (G + S + 1) (G – S)
• 計算量: O(1)
Div2 Hard : TorusSailingEasy
期待値の求め方(別解: a*b )
実は期待値は a * b になります。• 計算量: O(1)
Div2 Hard : TorusSailingEasy
期待値の求め方(別解: a*b ) (a + b) が偶数の場合• G = (a + b) / 2 , S = G – min(a, b)• G – S = min(a, b)• G + S = 2 * G – min(a, b) = a + b – min(a,b)• (G + S) (G – S) = (a + b – min(a,b)) * min(a,b)
= a * b
Div2 Hard : TorusSailingEasy
期待値の求め方(別解: a*b ) (a + b) が奇数の場合• G = (a + b - 1) / 2 S = G – min(a, b)
• G – S = min(a, b)• G + S + 1 = 2 * G – min(a, b) + 1 = a + b – min(a,b)• (G + S + 1) (G – S) = (a + b – min(a,b)) * min(a,b)
= a * b
Div2 Hard : TorusSailingEasy
解答例 (C#)
int reach(int N, int M, int x, int y) {
for (int p = x; p < N * M; p += N) if (p % M == y) return p;
return -1;
}
public double expectedTime(int N, int M, int goalX, int goalY) {
var a = reach(N, M, goalX, goalY);
var b = reach(N, M, (N - goalX) % N, (M - goalY) % M);
if (a == -1 || b == -1) return -1;
return 1.0 * a * b ;
}
Div1 Hard : TorusSailing
問題• 格子状に N × M のエリアがある。• シエルは (0,0) からスタートする• シエルは、もし (x, y) にいる場合は
• (x % N, (y + 1) % M) • ((x + 1) % N, y % M)
のどちらに等確率で移動する• (goalX, goalY) に到達するまでの移動回数の期待値を求め
よ
Div1 Hard : TorusSailing
制約• 2 N 100≦ ≦• 2 M 100≦ ≦• 0 goalX N-1≦ ≦• 0 goalY M-1≦ ≦• (goalX, goalY) != (0, 0)
Div1 Hard : TorusSailing• 遷移グラフには、ループが存在するため
動的計画法を適用できない
Div1 Hard : TorusSailing• E[x, y] = (x, y) から (goalX,goalY) までの移動回数の期待値• E[goalX, goalY] = 0• E[i, j] = (E[i + 1, j] + E[i, j + 1]) / 2 + 1 (0 i ≦ < N, 0 j ≦ < M)
• 連立方程式を構成することができるが、変数の個数が N * M 、高々 10000 になるため、ガウスジョルダンやガウスの消去法 だと間に合わない
Div1 Hard : TorusSailing
• ↓ から ループがなくなると嬉しい
Div1 Hard : TorusSailing
•具体的には、赤矢印 のような一周して戻ってくるような遷移がなくなるとループがなくなってうれしい
Div1 Hard : TorusSailing
• X 座標または Y 座標が 0 であるような(N+M-1)箇所の座標の期待値を変数化する
• ループがなくなった!!!
X[0,0] X[1,0] X[2,0] X[3,0]
X[0,2]
X[0,1]
X[0,0]
Div1 Hard : TorusSailing• ループがなくなったため、動的計画法を使って
各マスの期待値を ( N + M – 1) 変数多項式 で表せる
• P[i,j] = ∑ (Ci,j[a,b] * Xa,b) + di,j (where a = 0 or b = 0) X0,0 X1,0 X2,0 X3,0
X0,2
X0,1
X0,0
P[3,2]P[2,2]P[1,2]P[0,2]
P[3,1]P[1,1]P[0,1]
P[3,0]P[2,0]P[1,0]P[0,0]
Div1 Hard : TorusSailingP[i,j] = (i,j) からゴールまでの 期待値を表す( N+M-1) 変数多項式
X0,j ( i = N ) P[i,j] = Xi,0 ( j = M ) 0 ( i = goalX, j = goalY) (P[i+1, j] + P[i, j+1]) / 2 + 1 (otherwise)
Div1 Hard : TorusSailingここでP[0,0] = X0,0 = ∑ (C00[a,b] * Xa,b) + d0,0
P[1,0] = X1,0 = ∑ (C10[a,b] * Xa,b) + d1,0
…P[N-1,0] = XN-1,0 = ∑ (CN-1,0[a,b] * Xa,b) + dN-1,0
P[0,1] = X0,1 = ∑ (C0,1 [a,b] * Xa,b) + d0,1
…P[0,M-1] = X0,M-1 = ∑ (C0,M-1[a,b] * Xa,b) + d0,M-1
となり、( N+M-1) 変数連立方程式が構築できる
Div1 Hard : TorusSailing例:サンプル1の場合 (N,M,goalX,goalY)=(2,2,1,1)P[1,1] = 0P[0,1] = (X0,0 + P[1,1]) / 2 + 1 = X0,0 / 2 + 1P[1,0] = (P[1,1] + X0,0) / 2 + 1 = X0,0 / 2 + 1P[0,0] = (P[1,0] + P[0,1]) / 2 = X0,0 / 2 + 2
P[0,0] = X0,0 = X0,0 / 2 + 2 …( 1 )P[0,1] = X0,1 = X0,0 / 2 + 1 …( 2 )P[1,0] = X1,0 = X0,0 / 2 + 1 …( 3 )(1)(2)(3) の3変数連立方程式を解くと X0,0 = 4 になる( ただし小さいケースのため、 (1) だけでも X0,0 は導出できる )
Div1 Hard : TorusSailing• 変数の個数は (N + M – 1) 、高々 199 個• ガウスジョルダン や ガウスの消去法でも十分間に合う• 連立方程式を解くことで
X[0,0] 、 X[1,0] … X[N-1,0] 、 X[0,1]…X[0,M-1] が全て求まる• X[0,0] がそのまま答え• 計算量 : O((N+M)^3)
• ちなみに (0,0), (1,M-1), (2,M-2), … のように斜めに変数化することで、変数の個数は max(N, M) まで減らせます。