二次元データの区間探索

線形探索メニュー【二次元データ 1 〜 2・ピクニック・三次元データ・二次元データの区間探索】

【二次元データ 1】コード

reader.on('close', () => {
   const [n, m] = lines[0].split(' ').map(Number);
   let ans = 0;  // 出力値を 0 で初期化
   for (let i = 1; i <= n; i++) {  // n 行の入力を…
      const s = lines[i];  // 1 行ずつ s で受け取り
      for (let j = 0; j < m; j++) { 
         if (s[j] === '.') {  // s を一文字ずつ調べて '.' なら…
            ans++;  // ans を加算
         }
      }
   }
   console.log(ans);
});
hogeちゃんの画像
文字列も 添字を使えば 一文字ずつ調べることができました。

【二次元データ 2】コード

reader.on('close', () => {
   const [n, m, k] = lines[0].split(' ').map(Number);
   let ans = 0;  // 出力値を 0 で初期化
   for (let i = 1; i <= n; i++) {  // n 行の入力を…
      const s = lines[i].split(' ').map(Number);  // 数値化して s (配列)で受け取り
      for (let j = 0; j < m; j++) {
         if (s[j] === k) {  // s[j] が k なら… 
            ans++;
         }
      }
   }
   console.log(ans);
});
hogeちゃんの画像

【STEP: 1】と ほぼ 同じコードです。‘.’の数を計算 k (数値)の数を計算 になったので 入力を半角スペース区切りの配列に変換し 更に要素を数値化して s で受け取りました。if文の条件式は s[j] === k になっています。

【ピクニック】コード

reader.on('close', () => {
   const [n, m] = lines[0].split(' ').map(Number);
   const A = [];  // n 行 ✕ m 列 の二次元配列
   let ans = 0;  // 出力値を 0 で初期化
   for (let i = 1; i <= n; i++) {
      A.push(lines[i].split(' ').map(Number));  // 入力を(' ')区切りの配列に変換し 各値を数値化してから A に保存
   }
   for (let i = 0; i < n - 1; i++) {
      for (let j = 0; j < m - 1; j++) {
         const [a, b, c, d] = [A[i][j], A[i + 1][j], A[i][j + 1], A[i + 1][j + 1]];
         // A[i][j] を a に代入し a の 右・下・右下の各値を b, c, d に代入
         if (a === b && a === c && a === d) {  // ab, c, d と一致すれば…
            ans++;  // ans を 1 加算
         }
      }
   }
   //console.table(A);
   console.log(ans);
});
hogeちゃんの画像

二次元配列の各値にアクセスするために 二重ループを使っています。二次元配列の値の確認はconsole.table()を使うとわかりやすいですね。

【ピクニック 2】コード

reader.on('close', () => {
   const [n, m, k] = lines[0].split(' ').map(Number);
   const A = [];  // 各区画の価値を保存
   let Max_price = 0;  // 価値の合計が最大となる場所の価値(0で初期化)
   for (let i = 1; i <= n; i++) {
      A.push(lines[i].split(' ').map(Number));  // 入力を(' ')区切りの配列に変換し 各値を数値化して A に保存
   }
  // ここから 配列 A の 二次元累積和(配列 S) を生成
   const S = [[]];  // A の累積和を保存する配列
   for (let i = 0; i <= m; i++) {
      S[0].push(0);
   }
   for (let i = 0; i < n; i++) {
      S.push([0]);
      for (let j = 0; j < m; j++) {
         S[i + 1].push(S[i + 1][j] + A[i][j] + S[i][j + 1] - S[i][j]);
      }
   }
  // ここまで 配列 A の 二次元累積和(配列 S) を生成
   for (let i = k; i <= n; i++) {
      for (let j = k; j <= m; j++) {
         const price = S[i][j] - S[i - k][j] - S[i][j - k] + S[i - k][j - k];
  // 各区画の価値を price に代入し Max_price と比較して price の値が大きければ
            if (price > Max_price) {
            Max_price = price;  // Max_priceprice で上書き
          }
       }
    }
   console.log(Max_price);
});
hogeちゃんの画像
価値の合計が最大となる場所の価値の合計なので 二次元累積和を使ってみました。

【三次元データ】コード

reader.on('close', () => {
   const [n, m, h, x] = lines[0].split(' ').map(Number);
   const a = []; 
   for (let i = 1; i <= h * n; i++) {
  // 配列 a に 配列化した入力値を保存し ニ次元配列を生成
      a.push(lines[i].split(' ').map(Number));
   }
   for (let i = 0; i < h; i++) {
      a.push(a.splice(0, n));
// 二次元配列 a の要素を先頭から n 個ずつ切り取り 要素数 h 個の二次元配列を生成 => a の末尾に追加
   }
   let ans = 0;
   for (let i = 0; i < h; i++) {  // h は配列 a の要素数
      for (let j = 0; j < n; j++) {  // n は配列 a[i] の要素数
         for (let k = 0; k < m; k++) {  // m は配列 a[i][j] の要素数
            if (a[i][j][k] === x) {  // a[i][j][k] が x なら…
               ans++;  // ans を加算
            }
         }
      }
   }
  console.log(ans);
});
hogeちゃんの画像

課題が三次元データになっているので 三次元配列を生成しました。三次元配列のデータにアクセスするために 三重ループを使っています。

【二次元データの区間探索】コード 1/2

reader.on('close', () => {
   const [n, m, k, x] = lines[0].split(' ').map(Number);
   const A = [];  // nm の敷地
   for (let i = 1; i <= n; i++) {
      A.push(lines[i].split(' ').map(Number));  // nm 列の敷地番号
   }
   for (let i = 0; i < n; i++) {
      for (let j = 0; j < m; j++) {  // nm 列の区画を探索して…
         if (A[i][j] <= x) {  // 区画の各値が x 以下(予約済のスペース)なら…
             A[i][j] = 0;  // 値を 0 で上書き
         }
      }
   }
  // ここから 配列 A の 二次元累積和(配列 S) を生成
   const S = [ [] ];
   for (let i = 0; i <= m; i++) {
      S[0].push(0);
   }
   for (let i = 0; i < n; i++) {
      S.push([0]);
      for (let j = 0; j < m; j++) {
         S[i + 1].push(S[i + 1][j] + A[i][j] + S[i][j + 1] - S[i][j]);
      }
   }
  // ここまで 配列 A の 二次元累積和(配列 S) を生成
   let ans = 0;
   for (let i = k; i <= n; i++) {
      for (let j = k; j <= m; j++) {
  // 二次元累積和(配列 S) の部分和 を site に代入
         const site = S[i][j] - S[i - k][j] - S[i][j - k] + S[i - k][j - k];
         if (!site) {  // site が 0(false)なら
            ans++;  // ans を 1 加算
         }
      }
   }
   console.log(ans);
});
hogeちゃんの画像

こちらの課題も二次元累積和を使いました。x 以下の番号は 全て 0 に置き換え 累積和が 0 であれば ans を 1 加算。

【二次元データの区間探索】コード 2/2

reader.on('close', () => {
   const [n, m, k, x] = lines[0].split(' ').map(Number);
   const A = [];  // nm の敷地
   for (let i = 1; i <= n; i++) {
      A.push(lines[i].split(' ').map(Number));  // nm 列の敷地番号
   }
    let ans = 0;
    for (let i = 0; i <= n - k; i++) {
        for (let j = 0; j <= m - k; j++) {  // A[i][j] は シートの左上
            let able_to_lay_sheet = true;  // シートを敷けるか否かの真偽値
            for (let h = 0; h < k; h++) {
                for (let w = 0; w < k; w++) {  // A[i][j] から右下に向かって k * k マスの範囲に…
                    if (A[i + h][j + w] > x) {  // 番号が x より大きい区画がある場合
                        able_to_lay_sheet = false;  // シートを敷くことができない
                    }
                }
            }
            if (able_to_lay_sheet) {  // able_to_lay_sheet が true なら…
                ans++;  // ans を加算
            }
        }
    }
    console.log(ans);
});
hogeちゃんの画像

C++ の解答コード例を参照しました。

コメント