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 : TorusSailing
P[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] = 0
P[0,1] = (X0,0 + P[1,1]) / 2 + 1 = X0,0 / 2 + 1
P[1,0] = (P[1,1] + X0,0) / 2 + 1 = X0,0 / 2 + 1
P[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) ま
で減らせます。

TopCoder SRM614 解説

  • 1.
  • 2.
    Div2 Medium :MinimumSquareEasy 問題 • N 個の二次元座標が与えられる ( 3 ≦ N ≦ 50 ) • このうち少なくとも N-2 個の点を 内側(境界上はNG)に含むような正方形の最小面積を求めよ • ただし、「正方形の辺は垂直または水平」 「正方形の頂点は整数座標」でないといけない
  • 3.
    Div2 Medium :MinimumSquareEasy 解法 • 「N個の点 から N-2 個選ぶ」ことと 「N個の点から除外する2個の点を選ぶ」 ことは同じ • 除外する2個の点の選び方は N * (N – 1) / 2 通り • N ≦ 50 のため、高々 1225 通り
  • 4.
    Div2 Medium :MinimumSquareEasy 解法 • N-2個の点集合全パターンについて、 N-2個の点全てを内側に含むような正方形の最小面積を出す • バウンディングボックスの辺のうち、長いほうを求める • 総計算量: O(N^3)
  • 5.
    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; }
  • 6.
    Div1 Easy :MinimumSquare 問題 • N 個の二次元座標が与えられる ( 2 ≦ N ≦ 100 ) • このうち少なくとも K 個の点を ( 1 ≦ K ≦ N ) 内側(境界上はNG)に含むような正方形の最小面積を求めよ • ただし、「正方形の辺は垂直または水平」 「正方形の頂点は整数座標」でないといけない
  • 7.
    Div1 Easy :MinimumSquare • 辺上がNG だとやや扱いづらいので 辺上もOK にした上であとで 辺の長さ+2 にする
  • 8.
    Div1 Easy :MinimumSquare • 正方形の左上座標の候補は (X[p], Y[q]) の N^2 通り (0 ≦ p ≦ N-1, 0 ≦ q ≦ N-1)
  • 9.
    Div1 Easy :MinimumSquare • 左上座標の候補についてそこからどれぐらい辺を伸ばせば、 K個以上の点を含めることができるかを探索すればよい
  • 10.
    Div1 Easy :MinimumSquare 辺の長さの探索方法はいろいろ • 辺の長さで二分探索 → 総計算量:O(N^3 log(辺の最大の長さ)) • 辺の長さの候補は実は N 個なのでそれらを全て調べる → 総計算量:O(N^4) • N個の辺の長さの候補に対して二分探索 • → 総計算量:O(N^3 log(N))
  • 11.
    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 整数型を使いましょう。
  • 12.
    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); }
  • 13.
    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 を返せ
  • 14.
    Div2 Hard :TorusSailingEasy 制約 • 2 ≦ N ≦ 10 • 2 ≦ M ≦ 10 • 0 ≦ goalX ≦ N-1 • 0 ≦ goalY ≦ M-1 • (goalX, goalY) != (0, 0)
  • 15.
    Div2 Hard :TorusSailingEasy ( a % N, a % M) = (goalX, goalY) であるような最小の自然数 a (-b % N, -b % N) = (goalX, goalY) であるような最小の自然数 b を使って、 「0 から +1 または -1 にランダム・ウォークしたとき、 a または –b に到達するまでの移動回数の期待値を求めよ」 と言い換えられる ab
  • 16.
    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 のため、どれでも十分間に合う
  • 17.
    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)となる。
  • 18.
    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)
  • 19.
    Div2 Hard :TorusSailingEasy 期待値の求め方(動的計画法) • グラフは左右対称 • 各地点からゴールまでの期待値も左右対称 • ということは対称な点同士を統合できる!
  • 20.
    Div2 Hard :TorusSailingEasy 期待値の求め方(動的計画法) グラフを変換する 折り返す 対称点を統合
  • 21.
    Div2 Hard :TorusSailingEasy 期待値の求め方(動的計画法) 変換後 • (a + b) が偶数の場合 • (a + b) が奇数の場合
  • 22.
    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
  • 23.
    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)
  • 24.
    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)
  • 25.
    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
  • 26.
    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)
  • 27.
    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)
  • 28.
    Div2 Hard :TorusSailingEasy 期待値の求め方(別解: a*b) 実は期待値は a * b になります。 • 計算量: O(1)
  • 29.
    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
  • 30.
    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
  • 31.
    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 ; }
  • 32.
    Div1 Hard :TorusSailing 問題 • 格子状に N × M のエリアがある。 • シエルは (0,0) からスタートする • シエルは、もし(x, y) にいる場合は • (x % N, (y + 1) % M) • ((x + 1) % N, y % M) のどちらに等確率で移動する • (goalX, goalY) に到達するまでの移動回数の期待値を求めよ
  • 33.
    Div1 Hard :TorusSailing 制約 • 2 ≦ N ≦ 100 • 2 ≦ M ≦ 100 • 0 ≦ goalX ≦ N-1 • 0 ≦ goalY ≦ M-1 • (goalX, goalY) != (0, 0)
  • 34.
    Div1 Hard :TorusSailing • 遷移グラフには、ループが存在するため 動的計画法を適用できない
  • 35.
    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 になるため、 ガウスジョルダンやガウスの消去法 だと間に合わない
  • 36.
    Div1 Hard :TorusSailing • ↓ から ループがなくなると嬉しい
  • 37.
    Div1 Hard :TorusSailing • 具体的には、赤矢印 のような 一周して戻ってくるような遷移がなくなると ループがなくなってうれしい
  • 38.
    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]
  • 39.
    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]
  • 40.
    Div1 Hard :TorusSailing P[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)
  • 41.
    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)変数連立方程式が構築できる
  • 42.
    Div1 Hard :TorusSailing 例:サンプル1の場合 (N,M,goalX,goalY)=(2,2,1,1) P[1,1] = 0 P[0,1] = (X0,0 + P[1,1]) / 2 + 1 = X0,0 / 2 + 1 P[1,0] = (P[1,1] + X0,0) / 2 + 1 = X0,0 / 2 + 1 P[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 は導出できる)
  • 43.
    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) ま で減らせます。