C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で2Dグリッドをシフトする方法【アルゴリズムと実装例】


m × n のサイズを持つ2次元グリッドと整数 k が与えられます。ここで、グリッドに対してシフト操作を k 回実行することを考えます。シフト操作の規則は以下のとおりです。

  • grid[i][j] の要素は grid[i][j + 1] へ移動します

  • 各行の末尾 grid[i][n – 1] の要素は、次の行の先頭 grid[i + 1][0] へ移動します

  • 右下隅 grid[m – 1][n – 1] の要素は、左上隅 grid[0][0] へ移動します

たとえば、次のようなグリッドがあるとします。

123
456
789

これを1回シフトすると、出力は次のようになります。

912
345
678

この問題は、「グリッド全体を1マスずつ右方向にローテーションし、あふれ出した要素を左上に戻す」という処理だと考えると理解しやすくなります。

解法のアプローチ

この問題は、以下の手順に沿って解くことができます。

  • シフト操作は行列を引数として受け取ります

  • n := 行数、m := 列数とし、x := 右下の要素を一時的に退避します

  • i を n – 1 から 0 まで降順にループします

    • j を m – 1 から 0 まで降順にループします

      • j = 0 かつ i > 0 の場合:G[i][j] := G[i – 1][m – 1](前の行の末尾の要素を代入)

      • そうでなく j > 0 の場合:G[i][j] := G[i][j – 1](左隣の要素を代入)

  • G[0][0] := x(退避しておいた右下の要素を左上へ配置します)

  • 続いて、以下の規則でシフト操作を呼び出します

  • k が 0 になるまで繰り返します

    • グリッド G を1回シフトする

    • k を 1 減らす

  • 最後にグリッド G を返します

C++による実装例

それでは、実際の実装を見てみましょう。以下のコードで動作を確認できます。

#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<vector<int> > v){
    cout << "[";
    for(int i = 0; i<v.size(); i++){
        cout << "[";
        for(int j = 0; j <v[i].size(); j++){
            cout << v[i][j] <<", ";
        }
        cout << "],";
    }
    cout << "]"<<endl;
}
class Solution {
public:
    void shift(vector<vector<int>>& grid){
        int n = grid.size();
        int m = grid[0].size();
        int x = grid[n-1][m-1];
        for(int i = n-1; i>=0; i--){
            for(int j = m-1;j>=0;j--){
                if(j == 0 && i>0){
                    grid[i][j] = grid[i-1][m-1];
                }
                else if(j>0){
                    grid[i][j] = grid[i][j-1];
                }
            }
        }
        grid[0][0] = x;
    }
    vector<vector<int>> shiftGrid(vector<vector<int>>& g, int k) {
        while(k--){
            shift(g);
        }
        return g;
    }
};
main(){
    Solution ob;
    vector<vector<int>> mat = {{1,2,3},{4,5,6},{7,8,9}};
    print_vector(ob.shiftGrid(mat, 1));
}

入力

{{1,2,3},{4,5,6},{7,8,9}}
1

出力

[[9, 1, 2],[3, 4, 5],[6, 7, 8]]

計算量と最適化のポイント

この実装では、1回のシフト操作に O(m × n) の時間がかかるため、全体の時間計算量は O(k × m × n) となります。その一方で、追加のメモリは不要であり、空間計算量は O(1) です。

また、覚えておくと便利な性質として、グリッドは m × n 回シフトすると元の状態に戻ります。したがって、処理前に k を k % (m × n) で置き換えることで、k が非常に大きい場合でも無駄な繰り返しを省略でき、効率を大幅に改善できます。

  1. C++でプロセスを強制終了する方法:BFSを使った実装解説

    n個のプロセスがあると仮定します。各プロセスには、PID(プロセスID)と呼ばれる一意の識別子が割り当てられており、さらにPPID(親プロセスID)も持っています。各プロセスが持てる親プロセスは1つだけですが、子プロセスは1つでも複数でも構いません。これはまさに木構造と同じ形です。PPIDが0になるプロセスは1つだけであり、それはそのプロセスに親が存在しないことを意味します。また、すべてのPIDは一意な正の整数です。問題の概要ここでは、2つの整数リストを使ってプロセスの一覧を表現します。1つ目のリストには各プロセスのPIDが含まれ、2つ目のリストにはそれに対応するPPIDが含まれます。このとき

  2. C++で解くリスのナッツ収集シミュレーション ― 最小移動距離を求めるアルゴリズム

    問題概要 1本の木、1匹のリス、そして複数のナッツがフィールド上にあります。それぞれの位置は2次元グリッドのセルで表現されます。この問題の目的は、リスがすべてのナッツを集めて木の下に1個ずつ運ぶときの最小移動距離を求めることです。 リスの行動には次の制約があります。 一度に持てるナッツは最大1個 移動は上下左右の4方向で、隣接するセルへのみ可能 距離は移動回数(ステップ数)で表される たとえば、入力が「高さ: 5 / 幅: 7 / 木の位置: [2,2] / リスの位置: [4,4] / ナッツ: [[3,0], [2,5]]」の場合、出力は 12 となります。 解法のポイント まず、