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

code

ABC202-B

問題「0,1,6,8,9からなる文字列Sがあり、それを180度回転させたものを出力」

つまりabcdeをedcbaにして、6を9に9を6にするんですが、初めは「そんなのできるの?」と疑いました(笑) 入力は「S」です。ここでは新しいJavaScript技reverseを使います。

let array = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’];
let reversed = array.reverse();
console.log(reversed);

その名の通りreverse、これで[‘e’, ‘d’, ‘c’, ‘b’, ‘a’]になります。これができたらあとはif文で6だったら9に、9だったら6に、で出来そうですね。例1「0601889」は「6881090」にします。

'use strict'
function main(input) {
  let S = input.trim().split("").map(Number); 
  
  for (let i = 0; i < S.length; i++) {
    if(S[i] === 9) {
      S[i] = 6;
    } else if (S[i] === 6) {
      S[i] = 9;
    }
  }
  const answer = S.reverse().join("");
  console.log(answer);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

見慣れない.join(“”)があります。実はこれもJavaScript技の1つで、「間に何も入らないようくっ付ける」を意味します。例えばjoin(“-”)だとハイフンでくっ付けることになり

let array = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’];
let joined = array.join(“-”);
console.log(joined);

これでa-b-c-d-eになります。join(“”)ではabcdeになることがイメージできるかと思います。for文でiはインデックス番号なので0から始めS.length例1では7なのでi<7まで回せば十分です。9を6に、6を9にしてからSをreverseして[6, 8, 8, 1, 0, 9, 0]でこれをjoin(“”)して6881090になります。

ABC201-B

問題「N個の山があり、i個目の山の名前はSi、高さはTiです。2番目に高い山のなまえは?」

入力は初めての形でNの数が決まっていないので

「N

S1 T1

S2 T2

.

.

.

Sn Tn」

こういう形になりますが、N以降はfor文でループを回すのでそこまで深く考えなくても大丈夫ですが、今回はJavaScript技が登場します。まず配列に要素を足していくpushは山の名前と高さを配列に後ろから格納します。

let colors = [‘red’, ‘green’, ‘blue’];
colors.push(‘pink’);
console.log(colors);
=>[‘red’, ‘green’, ‘blue’, ‘pink’];

になります。さらに207で出たsortを使って大きい順に並び変えて「2番目に高い山」なので前から2番目の情報を取り出します。

'use strict'
function main(input) {
  input = input.trim().split("\n");
  const N = Number(input[0]);
  let array = [];
 
  for (let i = 1; i <= N; i++) {
    let [x, y] = input[i].trim().split(" ");
    y = Number(y);
    array.push([x, y]);
  }
 
  array.sort((a, b) => b[1] - a[1]);
  console.log(array[1][0]);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

split(“\n”)(半角バックスラッシュn)改行で分けて、1行目を数値化しますが、parseInt(input[0], 10);でも可、空の配列arrayを用意して、そこに情報を入れます。for文でinput[0]はNなので、input[i]はinput[1]から始めるためi=1;  i<=Nまで回します。次にinput[i]をsplitして[ ‘Everest’, ‘8849’ ]の形にしますが、一応数字なのでyは数値化してarrayにpush([x,y])します。例1だと3回繰り返して、この時点でconsole.log(array);すると[ [ ‘Everest’, 8849 ], [ ‘K2’, 8611 ], [ ‘Kangchenjunga’, 8586 ] ]になり配列の中に配列が入ります。(既に例が大きい順になってますね…)

次に並び替えsortは降順なのでarray.sort((a, b) => b[1] – a[1]); ここで[1]は各高さの数字が入り比較し、大きい順に並んだarrayの2番目[ ‘K2’, 8611 ],の最初の要素なのでarray[1][0]を出力します。

ABC200-B

問題「整数NにK回ある操作を行う。Nが200の倍数なら200で割る。でなければ、Nの後ろに200を付ける」

入力は「N K」  分かりにくいですが、例1は「2021 4」①2021は200の倍数でないので2021200になる、②2021200は200の倍数なので割って10106になる、③10106は違うので10106200、④10106200は200の倍数なので割って出力するのは「50531」このような操作をK回行う。

'use strict'
function main(input) {
  input = input.trim().split(" ");
  let N = Number(input[0]);
  let K = Number(input[1]);
 
  while(K > 0) {
    if(N % 200 === 0) {
      N /= 200;
    } else {
      N = (N * 1000) + 200;
    }
    K--;
  }
  console.log(N);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

for文を使う時のlet i = 0;などの定義が必要ないので、これはwhile文で書きました。回数Kを減らすイメージです。if文で剰余算「Nが200で割り切れるなら」Nを200で割り、そうでないならNに1000掛けて200を足す。200のために0を3つ用意してあげる感じでしょうか?忘れずにKをデクリメントし、Kが1を下回ったらwhile終わり。while(K >= 0)だと1回多く操作を行い「50531200」になるので注意。

ABC199-B

問題「長さNの数列A=(A1, A2, A3,…AN), B=(B1, B2, B3,…BN)がある。1≦ i ≦Nを満たす全ての整数iについてAi≦ x ≦Bi、xの個数は?」

問題文もよくわからないし、入力も新しい形

「N

A1, A2, A3 … AN

B1, B2, B3 … BN」

例1を見ると

「2

3 2

7 5」

3≦x≦7と2≦x≦5を満たすxは3,4,5の3つなので出力は「3」なんですが、いくつか線AiBiがあってその重なる部分を求めよ、ということです。

    Ai—–Bi

Ai————-Bi

      Ai—————–Bi

図にするとこんな感じでこの重なりはAiの最大値とBiの最小値の範囲になります。

'use strict'
function main(input) {
  input = input.trim().split("\n");
  let items = input.map((item)=>item.split(" ").map(Number));
  const N = items[0];
  const A = items[1];
  const B = items[2];
  
  const MAX_A = Math.max(...A);
  const MIN_B = Math.min(...B);
  let count = MIN_B - MAX_A + 1;
 
 if(count < 0) {
   count = 0;
   console.log(count);
 } else {
   console.log(count);
 }
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

新しいのはMath.maxですがこれは「最大を出してね」で想像つくかと思います。使い方は注意console.log(A)だと[3, 2]になりconsole.log(…A)だと3 2になり、配列ではなく数値で使用するため「…」スプレッド構文で展開します。

例1では3≦x≦5なんですが、5-3では2になるので+1が必要になり、let count = MIN_B – MAX_A + 1; さらにif文でcountが重ならない0より小さい時はマイナスではなくcount=0にする。

ABC198-B

問題「整数Nがあり、Nの先頭に0個以上の0をつけて回文にできるか?」

例1では「1210」先頭に0を1つで「01210」で回文となり”Yes”で、また0個以上なので例2「777」は0を付けることなく「777」で”Yes”

入力は「N」だけなので改行で分ける必要はありません。末尾に0がある場合は先頭に0を付けられるということなので、回文の判定に必要なく、削ってしまいます。つまりNが10で割り切れるなら、かつ0でなければ、Nを10で割り続けます。例1ではこの時点で「121」なんですがこれは数値型であり、split()はString文字列を指定した文字列で区切るメソッドなのでNを文字列に変換。ちなみにtyprof(N)でnumberやstringを確認できます。ここまでで前半。

'use strict'
function main(input) {
   let N = Number(input.trim());

  while((N % 10 === 0) && (N !== 0)) {
    N /= 10;
  }
    N = String(N);
  
  const reverse_N = N.split("").reverse().join("");
  if(N === reverse_N) {
    console.log("Yes");
  } else {
    console.log("No");
  }
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

0を取り除いた文字列Nをsplit(“”)で区切りこの時点で「[‘1’, ‘2’, ‘1’]」になり、これをreverseでひっくり返し、それをjoinで繋げます。「どの型がsplitできる、この型ではreverseできない」などデータ型の扱いは慣れないと難しい…

このひっくり返したreverse_Nと入力されたNが等しければ”Yes”

197-Bは難しすぎるし、文字にできないのでパス…

4 4 2 2

##..

…#

#.#.

.#.#

の左上から(2,2)の位置から見える「.」の数は?のやつです。上下左右それぞれfor文の中にif文書いて”#”でなかったら

ABC196-B

問題「整数または少数Xがあり、小数点以下切り捨てて整数で出力」

制約は0≦X≦10**100

「Math.floor(input)じゃダメなのか?」と思いましたが、大きい数字の時は上手くいかないようで、アルゴリズムは「.」で区切って前の整数部分を出力します。trimやsplit(改行)もなく書けます。

'use strict'
function main(input) {
  const N = input.split(".")[0];
  console.log(N);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

typeof(input)で方を確認するとstringなのでその後のsplitが使えます。ちなみにinput.split(“.”)[1]は90です。

ABC195-B

問題「重さA~Bグラムのみかんがたくさん(整数とは限らない)、いくつか選んで重さWになった。個数として考えられる最小値・最大値を求めよ。合う個数がない時はUNSATISFIABLE」

問題文も例を見てもイメージしにくい問題です。例2の入力「120 150 2」みかん1個が120~150グラムで、これで2キロを作りますが、単位が違うのでWは1000倍します。個数を多くするには軽い方だけで2キロを作るためW割るA、反対に個数を減らすには重い方だけで2キロを作るためW割るB、なんですが… W÷A=16.6666…なので17個には届かないため小数点以下切り捨てます。W÷B=13.3333…個が必要で、つまり13個では2キロを作れないため切り上げて14にします。

'use strict'
function main(input) {
  input = input.trim().split(" ");
  let A = Number(input[0]);
  let B = Number(input[1]);
  let W = Number(input[2]);
  W *= 1000;
 
  const max = Math.floor(W/A);
  const min = Math.ceil(W/B); 
 
  if(min > max){
    console.log('UNSATISFIABLE');
  } else {
    console.log(min, max);
  }
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

if文の条件は同数は個数をそのまま出力するのでmin>maxの時だけ“UNSATISFIABLE”

ABC194-B

問題「N人の従業員、仕事A・Bを終えたい。従業員iは仕事AをAi分、仕事BをBi分でこなす。AとBに1人従業員を割り当てるが、同じ従業員でもいい。その場合それぞれの時間が掛かる。2つの仕事が終わる最短時間は?」

「3

8 5

4 4

7 9」

例1では仕事Aの最小は従業員2の4、Bの最小は4ですが同じ従業員2の4になってしまい、4+4で8分になってしまいます。よってBは従業員1に任せ5分となり、2人による別作業なので2つの仕事が終わるのは5分です。

「3

11 7

3 2

6 7」

例2は両方の仕事を従業員2に任せて3+2=5が最小になります。ここでアルゴリズムは複雑になりそうなことが想像できます…まずNとそれ以外に分けますが、スプレッド構文でまとめる書き方[N, …AB]をします。他の問題でもlet ans = 0を用意しておいて、あとからansを更新するものがありましたが、今回のように小さいものが求められる場合let min = 10001000など明らかに大きい数を用意しておいて、最終的に小さいものを出力するケースがあります。

'use strict'
function main(input) {
  let [N, ...AB] = input.trim().split("\n");
  let min = 10001000;
  let sum_min = 0;
 
  for(let i = 0; i < N; i++) {
    for(let j = 0; j < N; j++) {
      let A = Number(AB[i].split(" ")[0]);
      let B = Number(AB[j].split(" ")[1]);
      
      if(i === j) {
        sum_min = A + B;
      } else {
        sum_min = Math.max(A, B);
      } 	
      min = Math.min(min, sum_min);
    }
  }
  console.log(min);
}
main(require('fs').readFileSync('/dev/stdin', 'utf8'));

console.log(AB)は[‘8 5’, ‘4 4’, ‘7 9’]になり、console.log(…AB);は「8 5 4 4 7 9」です。

続いて2重ループで全ての要素を比較しますが、if文の下でconsole.log(sum_min)を出してみると「13 8 9 5 8 9 7 7 16」と流れが分かりやすいです。まずA=8のケースは8と5ですがこれはi===jなので(従業員が同じ)8+5=13、次は8と4を比べMath.max(8,4)で8、次は8と9を比べMath.max(8,9)で9、A=4のケースは4と5で5、4と4(従業員同じなので)4+4=8、4と9は9、このようなイメージです。

そして最初は10001000と13を比べMath.minなので小さい方13がminになり、毎回minとsum_minを比べ小さい方がminに入っていき、最終的にminを出力します。

コメント

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