SRM287 - 気分は中学生?連立2元方程式
SRM287 に挑戦. 1 ヶ月ぶりの参戦.
TwoEquations (code)
300 点問題.以下のような 2 つの文字列が与えられます.
"1*X + 2*Y = 6"
"1*X + (-4)*Y = (-3)"
連立 2 元方程式です.「中学生か俺は?」でした..これを解いて,解が唯一存在するならば,以下のように解を文字列で
"X=(-7)/3 Y=3/4"
解がないならば
"NO SOLUTIONS"
を,解が複数存在するならば
"MULTIPLE SOLUTIONS"
を返しなさい.という問題です. 250 点問題ではなく 300 点問題ですのでちょっと面倒です.ですがそれほど難しい問題ではないと一見思ったのですが..
方針としては,各係数 A1-A3, B1-B3
A1 * X + A2 * Y = A3
B1 * X + B2 * Y = B3
をパースしてから,
int det = A1 * B2 - A2 * B1;
int x = B2 * A3 - A2 * B3;
int y = A1 * B3 - B1 * A3;
を計算して,det == 0 ならば,「解がない」 か 「解が複数」
if (det == 0) {
if (x == 0) {
return "MULTIPLE SOLUTIONS";
}
return "NO SOLUTIONS";
}
それ以外ならば, 「解がひとつ」ということで,
"X=x/det Y=y/det"
として,約分等・正しくフォーマットして返せばよい.これでいけると思って Submit したのですが,甘かったです.
これだけでは,以下のような係数がゼロの場合に対処できませんでした.
"0*X + 0*Y = 1"
"0*X + 0*Y = 2"
この場合は,正しい正解は"NO SOLUTIONS"ですが,上記ロジックだけだと"MULTIPLE SOLUTIONS"を返してしまいます.
以下のように係数ゼロを特別に扱う必要性がありました.
if (A1 == 0 && A2 == 0 && A3 != 0) return "NO SOLUTIONS";
if (B1 == 0 && B2 == 0 && B3 != 0) return "NO SOLUTIONS";
if (A1 == 0 && B1 == 0 && (A2 * B3 - B2 * A3) != 0) return "NO SOLUTIONS";
if (A2 == 0 && B2 == 0 && (A1 * B3 - B1 * A3) != 0) return "NO SOLUTIONS";
私も含めて多くの人はゼロのケースを考慮しない Code を書いてしまったようです.これに気づいた人は,チャレンジタイムで大稼ぎしていました.
この問題の Division1 での正解率は 11%,同じ問題が Division2 のレベル 2 問題にも使用されていましたが Division2 ではなんと正解率 0%,誰ひとり正解できなかったようです..
MooresLaw (code)
450 点問題.
「ムーアの法則」: コンピュータのスピードは,18 ヶ月で 2 倍になる
今,計算を始めると 14 年かかる問題があるとします.今すぐ始めるのではなく,18 ヶ月待ってスピードが 2 倍の新しいコンピュータを買ってそれで計算を始めると 7 年ですみます. 14 年かかる計算が,合計 (1.5 + 7 = ) 8.5 年ですむわけです.この例だと,約 4 年まってから計算を始めるのがベストです.合計( 4 + 2.2 = ) 6.2 年ですみます.
このように,計算にかかる年数 (years) が与えられた場合,ムーアの法則が成り立つとすると,最短・今から何年後に計算を終わらせることができるでしょうか?という問題です.いつ新しいコンピュータを買い計算をはじめるか?がポイントになります.
x 年後に,新しいコンピュータを買って計算を始めるとすると,トータルでは
y = x + (years / 2^(x / 1.5) )
かかることになります.この y が最小になる x を探せばよいことになります.数学的に解くことはできるのでしょうが(微分してゼロになるところを求める)..数学的に悩むよりは,このような U 字型の関数の場合は, いわゆる binary search ではなく,ternary search で,y が最小になる x を探すことができます.
区間 ( [left, right] ) 内の 2 点(x1, x2)をとり,y1, y2 を計算して,
- y1 > y2 ならば,left = x1
- y1 < y2 ならば,right = x2
として,区間を狭めていけば,最終的には,最小の y をとる xa にたどり着きます.
public class MooresLaw {
public double shortestComputationTime(int years) {
double left = 0;
double right = years;
while (right - left > 1e-11) {
double x1 = (left * 2 + right) / 3.0;
double x2 = (left + right * 2) / 3.0;
if (cal(x1, years) > cal(x2, years)) {
left = x1;
} else {
right = x2;
}
}
return cal(right, years);
}
double cal(double x, int years) {
return x + years / Math.pow(2, x / 1.5);
}
SRM 中は,x 年後のコンピュータのスピードが,Math.pow(2, x / 1.5) で表せることにきずかず,前半部分・回りくどいことをしていまいました...なんとか正解しましたが.
CoinGame
1000 点問題. Open したものの,手が出ず..
結果
System Test の結果です. ( Room Statistics )
全体的にポイントが低かったためか,レーティングは 1542 -> 1626 と上昇しました.