競技プログラミング「AtCoder」をJavaScriptでやってみた その7

code

ABC176-B

問題「整数Nが9の倍数であるなら、Nの各桁の和は9の倍数です。Nが9の倍数か判定」

入力がNは0~10**2000000までと大きいのでBigInt かな?と思いましたが、Numberでできました。入力は「N」だけです。178-Bでは「a b c d」と複数だったのでその違いもあるのかもしれません。

例1は「123456789」の和は45で、45は9の倍数なので”Yes”になります。「18」の和が9になって18は9の倍数になる、こういうことです。

'use strict'
function main(input) {
  input = input.trim().split("\n");
  let N = input[0].split("").map(Number);
  let total = N.reduce((a, b) => a + b);
  
  if (total % 9 === 0) {
    console.log("Yes");
  } else {
    console.log("No");
  }
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

改行のsplitは要らなかった気もしますが、各桁を分解するので.split(“”)で[1, 2, 3, 4, 5, 6, 7, 8, 9]になり、これをreduceで最初は累積値aは0なので(a, b)=(0, 1)の0+1、次は(1, 2)の1+2と足し上げていきます。これも

for (let i = 0; i < N.length; i++) {
    total += N[i];
  }

でもよかったと思います。あとはtotalが9で割り切れるなら”Yes”を出力。何やら64ビット整数がBigIntでは自然に扱いやすいそうで、BigInt同士なら計算もできるとのこと。

ABC175-B

問題「1,2,3…Nと番号が付いたN本の棒があり、棒iの長さはLiです。三角形を作れる長さの異なる3本の棒の選び方は何通りか?」

「N
L1 L2 … LN」

問題文にもありますが、つまりはLi, Lj, Lkの長さが全て異なる三角形を作る、その棒の選び方は何通りか?ということですが、ここにない重要な要素として、例えば(1, 2, 10)の3本では三角形は作れないということです。「全て異なる」だけでは不十分というわけです。例1では

「5
4 4 9 7 5」

の5本があり、条件を満たす(i, j, k)は(1, 3, 4), (1, 4, 5), (2, 3, 4), (2, 4, 5), (3, 4, 5) の 5個になりますが、これは棒の番号であり長さではありません。4が2本あるので(1, 3, 4)と(2, 3, 4)がカウントされる点に注意。iに対してjとkを判定することになるので3重ループになります。

'use strict'
function main(input) {
  input = input.trim().split("\n");
  let N = input[0].split(" ").map(Number);
  let L = input[1].split(" ").map(Number);
  let count = 0;
  
  for(let i = 0; i < N - 2; i++) {
    for(let j = i + 1; j < N - 1; j++) {
      for(let k = j + 1; k < N; k++) {
        if(L[i] !== L[j] && L[j] !== L[k] && L[k] !== L[i]  ) {
          if(L[i] < L[j] + L[k] &&
             L[j] < L[k] + L[i] &&
             L[k] < L[i] + L[j]) {
            count++;
          }
        }
      }
    }
  }
  console.log(count);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

値を取り出し配列にするところまではいつも通りだと思いますが、for文の条件も複雑でif文もネストが深くなっています…

例1で解説しますが、i = 0; i < N – 2なので[4 4 9]までしかループしません。これはL[i]=9の時点でL[j]=7とL[k]=5とも判定しているのでその先は必要ないことを意味します。jは1つズレて[4 9 7]だけ担当すればいい、kはさらに1つズレて[9 7 5]まで担当し、N=5まで回す必要はなく「k < N」になります。

if文は読みにくいですが「長さが全て異なる時」という意味です。さらにその中のif文は「1本が他の2本の和より短い時」この条件が揃ってcount++でインクリメントします。3重ループは紙に書いたりも大変なので難問だと思います。

ABC174-B

問題「2次元平面上にN個の点があり、i個目の座標は(Xi, Yi)です。原点からの距離がD以下である点は何個か?座標(p, q)と原点の距離は√p**2+q**2 」

「N D
X1 Y1
.
.
XN YN」
「4 5
0 5
-2 4
3 4
4 -4」

が例1ですがそれぞれ2乗して足して平方根、例えば√3**2+4**2=5となり、5以下なので5も含まれ答えは3です。

'use strict'
function main(input) {
  input = input.trim().split("\n");
  let items = input.map((item)=>item.split(" ").map(Number));
  let N = items[0][0];
  let D = items[0][1];
  let ans = 0;
  
  for(let i = 1; i <= N; i++) {
    let square = Math.sqrt(items[i][0] * items[i][0] + items[i][1] * items[i][1]);
    if (square <= D) {
      ans++
    }
  }
  console.log(ans);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

改行してそれぞれ数値化して配列なのでitemsは[ [ 4, 5 ], [ 0, 5 ], [ -2, 4 ], [ 3, 4 ], [ 4, -4 ] ]となってます。N, Dとans = 0を用意して、for文は2行目からの情報が欲しいのでlet I = 1からNまでループします。次がポイントの平方根ですが、要素を2乗したもの同士を足して、それをMath.sqrt()で平方根かけたものをsquareと定義して、if文でそれがD以下ならans++でインクリメントします。以下なので「<=」でなくてはいけません。そこまでをループするので1行ずつこの判定をしています。

ABC173-B

問題「プログラミングコンテストに解答提出、N個のテストケースに結果SiがAC,WA,TLE,REのそれぞれの個数を求めよ」

「N
S1
.
.
SN」

と入力はよく見るものですが、今回は出力も厳密に決められていて

「AC x C0
WA x C1
TLE x C2
RE x C3」

の形でいくつあったかを解答します。特に例を見る必要もないので

'use strict'
function main(input) {
  input = input.trim().split("\n");
  let N = Number(input[0]);
  let ac = 0;
  let wa = 0;
  let tle = 0;
  let re = 0;
  
  for(let i = 1; i <= N; i++) {
    if(input[i] === "AC") {
      ac++;
    }
    if(input[i] === "WA") {
      wa++;
    }
    if(input[i] === "TLE") {
      tle++;
    }
    if(input[i] === "RE") {
      re++;
    }
  }
  console.log(`AC x ${ac}`);
  console.log(`WA x ${wa}`);
  console.log(`TLE x ${tle}`);
  console.log(`RE x ${re}`);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

値は1列で送られてくるsplit(“ ”)は書いていません。変数ac,wa,tle,reを0で用意して大文字ACにすると文字列“AC”と見間違えることもあるためacと小文字にしました。for文は2行目からなのでi = 1からNまでループします。if文はelse ifで繋いで書くこともできますが、シンプルなif文なので4つ分けた方が読みやすい気がしたので。

出力は文字列と変数を合体させるためいつもと異なります。console.log(‘AC ‘ + ‘x ‘ + ac);でも正解だと思いますが、今回はテンプレートリテラルを使ってconsole.log(`AC x  ${ac}`);にしました。

ちなみに別解答ですがいつもの調子でsplit(“ ”)を書いても出来ましたが、注意が必要

'use strict'
function main(input) {
  input = input.trim().split("\n");
  let items = input.map((item)=>item.split(" "));
  let N = items[0].map(Number);
  let ac = 0;
  let wa = 0;
  let tle = 0;
  let re = 0;
  
  for(let i = 1; i <= N; i++) {
    if(items[i].toString() === "AC") {
      ac++;
    }
    if(items[i] == "WA") {
      wa++;
    }
    if(items[i] == "TLE") {
      tle++;
    }
    if(items[i] == "RE") {
      re++;
    }
  }
  console.log(`AC x ${ac}`);
  console.log(`WA x ${wa}`);
  console.log(`TLE x ${tle}`);
  console.log(`RE x ${re}`);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

これで正解と同じ出力ですが、注目は.toString()の点です。「===」が型まで同じかを判定するので例えばif(items[i] === “WA”)ではifが成立しないのでカウントされず「WA x 0」になります。typeof(items[i])で確認するとobjectですので「===」では文字列stringと別のものと判定されます。

ABC172-B

問題「文字列S, Tがあり、Sの1文字を別の文字に書き換えてTに変更するときの最小回数は?」

制約はS,Tは英小文字、SとTの長さは等しい。ここまでこの記事を読んでいただいている方は問題文を読んで簡単そうと思えるはずです。難易度は低めです。例には「1 回目:6 文字目の c を h に書き換える」とありますが、文字を正す必要はなく「何回操作が必要か」なので異なる箇所をカウントするだけです。

'use strict'
function main(input) {
  input = input.trim().split("\n");
  let S = input[0];
  let T = input[1];
  let ans = 0;

  for(let i = 0; i < S.length; i++) {
    if(S[i] !== T[i]) {
      ans++;
    }
  }
  console.log(ans);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

特に解説するようなポイントもないですが、for文の条件は横に文字がズレていくのでi = 0からS.lengthと書きましたがT.lengthでも大丈夫です。先頭がS[0]になるので要素数より1つ少なく「<=」ではなく「<」です。

コメント

タイトルとURLをコピーしました