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

C++で2本の指を使って単語を入力するための最小距離

以下のようなキーボードレイアウトがあるとします。

ABCDEF
GHIJKL
MNOPQR
STUVWX
YZ

各大文字アルファベットは座標に配置されています。例えば、Aは(0,0)、Bは(0,1)、Pは(2,3)、Zは(4,1)にあります。与えられた単語を2本の指のみで入力する際の最小総移動距離を求めます。2地点(x1,y1)と(x2,y2)間の距離はマンハッタン距離 |x1-x2| + |y1-y2| で定義され、開始位置はキーボード上の任意の場所から始められます。

問題の例

入力が "HAPPY" の場合、出力は6となります。

  • Hから開始(コスト0)
  • Aへ移動:H(1,1)→A(0,0) でコスト2
  • Pへ移動:もう片方の指でP(2,3)を押す(コスト0)
  • 再びP:同じ指で押す(コスト0)
  • Yへ移動:P(2,3)→Y(4,1) でコスト4

総コスト = 2 + 4 = 6

解法アプローチ

メモ化再帰(動的計画法)を用いて解きます。状態は「指1の位置、指2の位置、現在処理中の文字インデックス」で定義されます。

主要な関数

  • getXY(char c): 文字から座標(row, col)を取得。'A'からのオフセットを6で割った商と余りで計算。
  • getDist(x1,y1,x2,y2): 2点間のマンハッタン距離。未配置(-1,-1)の場合は距離0。
  • solve(x1,y1,x2,y2,word,idx): 再帰関数。現在の文字を指1または指2のどちらで打つか最小コストを探索し、メモ化する。
  • getHash(...): 状態を一意の整数キーに変換(メモ化用)。

C++実装例

#include <bits/stdc++.h>
using namespace std;

class Solution {
    map<int, int> memo;
    
    int getHash(int a, int b, int c, int d, int e) {
        int temp = 0;
        while (a) { temp = temp * 10 + a % 10; a /= 10; }
        while (b) { temp = temp * 10 + b % 10; b /= 10; }
        while (c) { temp = temp * 10 + c % 10; c /= 10; }
        while (d) { temp = temp * 10 + d % 10; d /= 10; }
        while (e) { temp = temp * 10 + e % 10; e /= 10; }
        return temp;
    }
    
    pair<int, int> getXY(char c) {
        int a = c - 'A';
        return {a / 6, a % 6};
    }
    
    int getDist(int x1, int y1, int x2, int y2) {
        if (x1 == -1 && y1 == -1) return 0;
        return abs(x1 - x2) + abs(y1 - y2);
    }
    
    int solve(int x1, int y1, int x2, int y2, string &word, int idx) {
        if (idx == word.size()) return 0;
        
        int state = getHash(x1 + 2, y1 + 2, x2 + 2, y2 + 2, idx + 2);
        if (memo.count(state)) return memo[state];
        
        auto [nx, ny] = getXY(word[idx]);
        
        int cost1 = getDist(x1, y1, nx, ny) + solve(nx, ny, x2, y2, word, idx + 1);
        int cost2 = getDist(x2, y2, nx, ny) + solve(x1, y1, nx, ny, word, idx + 1);
        
        return memo[state] = min(cost1, cost2);
    }
    
public:
    int minimumDistance(string word) {
        memo.clear();
        return solve(-1, -1, -1, -1, word, 0);
    }
};

int main() {
    Solution ob;
    cout << ob.minimumDistance("HELLO") << endl; // 出力: 4
    return 0;
}

実行結果

入力"HELLO"
出力4

計算量

  • 時間計算量: O(N × 26²) ≈ O(N) — Nは単語の長さ。状態数は文字位置の組み合わせ(26×26)とインデックス(N)の積。
  • 空間計算量: O(N × 26²) — メモ化テーブルのサイズ。
  1. C++のインクリメント演算子(++)を使って2つの数値を加算する方法

    プログラミングにおける ++ 演算子は、オペランドの値を1だけ増やす「インクリメント演算子」です。実は、この演算子を繰り返し使うことで、加算演算子(+)を使用せずに2つの数値を足し合わせることができます。具体的な考え方はシンプルです。片方の数値 a に対して、もう一方の数値 b の回数だけ 1 を加算すれば、a と b の合計が求まります。処理の例入力:a = 31 , b = 4 出力:35解説: 31に1を4回加えるため、31 + 1 + 1 + 1 + 1 = 35 となります。アルゴリズム入力:2つの整数 a と b ステップ1:0 から b までループし、各回でステップ2を実行する

  2. C++で最小限の比較回数により3つの値の中央値を求める方法

    この記事では、与えられた3つの値を比較することで、その中間(中央)の値を求める方法を解説します。例えば、3つの数値 (10, 30, 20) が与えられた場合、中央の値である 20 を返します。まずアルゴリズムの流れを確認し、その後、実際にC++コードとして実装していきましょう。アルゴリズム3つの値 a, b, c の中間値を求める手順は以下の通りです。ポイントは、比較を最小限の回数(最大でも3回)で済ませることにあります。 c, then return c else return b Endアルゴリズムの考え方まず a と b