-
【C++入門】定価と販売価格から割引率を求めるプログラムの作成方法
問題概要 この問題では、ある商品の定価(M)と販売価格(S)を表す2つの数値が与えられます。私たちの課題は、C++を用いて割引率を求めるプログラムを作成することです。 割引とは 割引とは、商品の実際の価格(定価)から差し引かれる金額のことを指します。 割引額を求めるための計算式は以下の通りです。 割引額 = 定価 − 販売価格 割引率とは 割引率とは、商品の実際の価格に対してどれだけの割合が割り引かれたかを示す数値です。 割引率を求めるための計算式は以下の通りです。 割引率 = (割引額 ÷ 定価) × 100 計算例 具体的な数値を使って問題を確認してみましょう。 入力 240, 1
-
C++で正整数Nを連続する正整数の和として表す方法の数を求める
正整数 N が与えられたとき、N を「1 つ以上の連続する正整数の和」として表現できるパターンが何通りあるかを求める問題を考えます。例として、入力が 10 の場合を考えてみましょう。10 は次の 2 通りで表現できます。10 = 1 + 2 + 3 + 4(4 つの連続する整数の和)10 = 10(数そのもの 1 つ)したがって、この場合の出力は 2 となります。解法の考え方N を「a から始まる i 個の連続する正整数の和」として表すことを、数学的に整理してみます。a + (a + 1) + ... + (a + i − 1) = i × a + i × (i − 1) / 2 = Nこの式
-
C++で木構造における距離の総和を効率的に求める方法
N個のノードからなる無向かつ連結な木を考えます。ノードには0〜N-1のラベルが付いており、N-1本の辺が与えられています。i番目の辺は、ノードedges[i][0]とedges[i][1]を結んでいます。このとき、ans[i]が「ノードiとそれ以外のすべてのノードとの間の距離の合計」を表すようなリストansを求めるのがこの記事の目的です。たとえば、入力がN = 6、edges = [(0,1),(0,2),(2,3),(2,4),(2,5)]である場合、出力は[8,12,6,10,10,10]となります。解法のアプローチこの問題を効率的に解くには、「再根付け(rerooting)」と呼ばれるテ
-
C++で解く「類似文字列グループ」問題 ― Union-Findを使った効率的な実装
問題の概要2つの文字列 X と Y が「類似(similar)」しているとは、次のいずれかの条件を満たすことを指します。X の2文字を入れ替えることで Y と等しくできるX と Y が完全に一致している例を挙げてみましょう。「tars」と「rats」は t と r を入れ替えることで互いに変換できるため類似しています。また、「rats」と「arts」も r と a を入れ替えることで一致するため類似しています。しかし、「star」は「tars」「rats」「arts」のどれとも1回の入れ替えでは一致しないため、類似していません。このとき、文字列たちは類似関係によって次の2つの連結グループを形成
-
C++でグラフの全ノードを訪問する最短経路の長さを求める方法
無向かつ連結なグラフがあり、N 個のノードには 0, 1, 2, ..., N-1 の番号が付けられているものとします。配列 graph の長さは N であり、graph[i] にはノード i と直接接続しているノード j(i ≠ j)がちょうど一度だけ含まれます。この課題では、すべてのノードを訪問する最短経路の長さを求めます。開始地点と終了地点は任意のノードを選べるほか、同じノードや辺を何度でも再訪問・再利用できる点が特徴です。たとえば、入力が [[1],[0,2,4],[1,3,4],[2],[1,2]] の場合、答えは 4 になります。このとき [0, 1, 4, 2, 3] という経路
-
C++で解く「K類似文字列」問題:最小スワップ回数をBFSで求める方法
問題の概要 2つの文字列 A と B が与えられます。A に含まれる2つの文字の位置をちょうど K 回入れ替えることで B と同じ文字列にできるとき、A と B は「K類似(K-similar)」であると定義されます。ここでは、互いにアナグラムの関係にある2つの文字列 A と B に対して、両者が K 類似となる最小の K を求めます。 たとえば、入力が A = "abc"、B = "bac" の場合、先頭の2文字を1回入れ替えるだけで B に変換できるため、答えは 1 になります。 解法の考え方:幅優先探索(BFS) この問題は幅優先探索(BFS)で
-
C++でK人の労働者を雇うための最小コストを求めるアルゴリズム
N人の労働者がいると仮定します。各労働者には作業の「質(quality)」を表すパラメータが割り当てられており、i番目の労働者は質 quality[i] と最低賃金の希望額 wage[i] を持っています。ここで、K人の労働者を雇って賃金支払いグループを結成することを考えます。K人のグループを雇用する際には、次の2つのルールを守る必要があります。グループ内の各労働者への支払いは、グループ内の他のメンバーと比較した質の比率に比例していなければなりません。グループ内のすべての労働者には、少なくともそれぞれの最低賃金の希望額以上を支払わなければなりません。私たちの目的は、これらの条件を満たすグループ
-
C++で合計がK以上となる最短の部分配列を求める方法
配列 A が与えられたとき、要素の合計が K 以上になる「最短の非空・連続する部分配列」の長さを求める問題を考えてみましょう。条件を満たす部分配列が存在しない場合は、-1 を返します。たとえば、入力が [5, 3, -2, 2, 1]、K = 6 の場合、出力は 2 になります。これは先頭の 2 要素を取ると 5 + 3 = 8 ≥ 6 となり、条件を満たすためです。解法のアプローチこの問題は、累積和(プレフィックスサム)と単調両端キュー(deque)を組み合わせることで効率的に解けます。配列に負の数が含まれる場合、通常のスライディングウィンドウでは正しく判定できないため、deque を使って
-
C++で解く「最小給油回数」問題 ― 貪欲法と優先度付きキューを使った効率的な解法
問題の概要ある車が出発地点から出発し、東に t マイル離れた目的地まで走行することを考えます。道中には複数のガソリンスタンドが点在しており、各 station[i] は「出発地点から東に station[i][0] マイルの位置にあり、station[i][1] リットルの燃料を備えたスタンド」を表します。車の燃料タンクの容量は無限で、出発時には startFuel リットルの燃料が入っています。車は 1 マイル走行するごとに 1 リットルの燃料を消費します。車はガソリンスタンドに到達すると、そこで立ち寄って給油することができ、そのスタンドの燃料をすべてタンクに移し替えられます。目的地に到達す
-
C++で解く「収益性のある計画」問題 ― 動的計画法による効率的なアプローチ
この記事では、「収益性のある計画(Profitable Schemes)」と呼ばれる動的計画法の定番問題を、C++を使って解く方法を詳しく解説します。 問題の概要 G人からなるギャングと、彼らが実行できるさまざまな「犯罪(仕事)」のリストがあるとします。i番目の犯罪は profit[i] の利益を生み出し、group[i] 人のメンバーを必要とします。 ここで重要な制約として、1人のメンバーは複数の犯罪に同時に参加できないというものがあります。 「収益性のある計画」とは、以下の2つの条件を満たす犯罪の部分集合として定義されます。 その部分集合から得られる合計利益が P以上 である その部分集
-
C++で解くスーパーエッグドロップ問題 ― 最小手数を求めるアルゴリズム
問題概要 K個の卵と、1階からN階まである建物が与えられます。すべての卵は同じ性能を持ち、一度割れた卵は二度と使用できません。 0以上N以下のどこかに臨界階Fが存在し、Fより高い階から卵を落とすと必ず割れ、F以下の階から落とした場合は割れないものとします。各手番では、卵を1つ選び、1〜Nの範囲内の任意の階Xから落とすことができます。 目的は、Fの値を確実に特定することです。Fの初期値が何であっても対応できるようにするには、最低何回の操作が必要でしょうか。 例えば、入力が K = 2、N = 6 の場合、出力は 3 となります。 解法の考え方 この問題は、動的計画法(DP)と二分探索を組み合わ
-
C++で部分列の幅の総和を効率的に求める方法
問題の概要整数の配列 A が与えられたとき、A のすべての空でない部分列を考えます。任意の列 S に対して、その「幅」は S に含まれる要素の最大値と最小値の差として定義されます。このとき、配列 A のすべての部分列の幅の総和を求めるのが課題です。答えは非常に大きな値になる可能性があるため、109 + 7 で割った余りを返します。たとえば入力が [3, 1, 2] の場合、出力は 6 になります。部分列は [1]、[2]、[3]、[2,1]、[2,3]、[1,3]、[2,1,3] の7通りであり、それぞれの幅は 0、0、0、1、1、2、2 となります。これらを合計すると 6 になるためです。解
-
C++で最大頻度スタック(FreqStack)を実装する方法
この記事では、整数を管理する特殊なスタック「FreqStack」をC++で実装する方法を解説します。FreqStackは、以下の2つの操作をサポートするデータ構造です。 push(x):整数xをスタックにプッシュします。 pop():スタック内で最も出現頻度の高い要素を取り除き、その値を返します。同じ頻度の要素が複数存在する場合は、スタックのトップに最も近い(最後に追加された)要素が取り除かれます。 例えば、7、9、7、9、6、7の順に要素をプッシュした後、pop()を4回呼び出すと、出力は7、9、7、6となります。 アルゴリズムの考え方 この問題は、ハッシュマップとスタックを組み合わせる
-
C++で解く整然キュー(Orderly Queue)問題:辞書順最小の文字列を求めるアルゴリズム
問題の概要 小文字のみで構成される文字列 S が与えられます。この文字列に対して、任意の回数だけ次の操作を行うことができます。 操作: 先頭の K 文字の中から1文字を選び、それを取り除いて文字列の末尾に移動させる。 このとき、操作を何度行ってもよいとして、最終的に得られる文字列のうち辞書順で最小のものを求めてください。 例えば、入力が cabaa、K = 3 の場合、答えは aaabc になります。 解き方のアプローチ ケース1:K > 1 の場合 Kが2以上のときは、実質的にどのような並べ替えも可能になります。これは、隣接する2文字の入れ替え(バブルソートと同じ要領)を繰り返すこと
-
C++で指定された数字集合から作れるN以下の整数の個数を求める方法
問題概要ソート済みの数字集合 D が与えられます。これは {'1', '2', '3', '4', '5', '6', '7', '8', '9'} の空でない部分集合であり、0 は含まれません。ここで、これらの数字を何度でも繰り返し使って数値を書くことを考えます。例えば、D = {'2','3','7'} の場合、「23」「771」「2372327」のような数値を書くことができます。このとき、この方法で
-
C++でDIシーケンスの有効な順列を数える方法
文字列 S を考えます。この文字列は集合 {D, I} に属する文字のみで構成されています。「D」は「減少(decreasing)」、「I」は「増加(increasing)」を意味します。 ここで、有効な順列とは、整数 {0 から n} の順列 P[0], P[1], ..., P[n] のうち、すべての i について次の規則を満たすものを指します。 S[i] == D の場合、P[i] > P[i+1] を満たす それ以外(S[i] == I の場合)、P[i] < P[i+1] を満たす 私たちの課題は、そのような有効な順列が何通り存在するかを求めることです。答えは非常に
-
C++で解くスーパーパリンドローム ― 区間内の「回文の平方数」を効率的に数える
正の整数 N がスーパーパリンドローム(superpalindrome)であるとは、次の2つの条件を同時に満たすことを指します。 N 自身が回文である(前から読んでも後ろから読んでも同じ数になる) N が「ある回文の平方」である(つまり N = k² となる k 自身も回文である) 本記事では、2つの正の整数 L と R が与えられたとき、閉区間 [L, R] に含まれるスーパーパリンドロームの個数を求める問題を、C++ の実装例とともにわかりやすく解説します。 たとえば、入力が L = 5、R = 500 の場合、出力は 3 となります。この範囲に含まれるスーパーパリンドロームは 9(
-
C++で解く「音楽プレイリストの総数」問題 ― 動的計画法による実装ガイド
問題の概要 N種類の異なる楽曲を収めたミュージックプレイヤーがあり、旅行中に合計L曲を聴きたいと考えます。このとき、次の条件をすべて満たすプレイリストを作成する必要があります。 すべての楽曲が少なくとも1回は再生されること ある楽曲をもう一度再生できるのは、その後にK曲以上の他の楽曲が再生されてからであること この条件を満たすプレイリストの総数を求めます。答えは非常に大きな値になる可能性があるため、109 + 7 で割った余りを返します。 たとえば、入力が N = 2、L = 3、K = 0 の場合、出力は 6 となります。これは [1,1,2]、[1,2,1]、[2,1,1]、[2,2,
-
C++で0と1の配列を等しい値の3つの部分に分割する方法
問題概要 0と1だけで構成された配列Aが与えられます。この配列を、すべて同じ2進数値を表す3つの空でない部分に分割することを考えます。分割が可能な場合は、i+1 < j を満たす添字のペア [i, j] を返してください。分割は次のようになります。 第1部分:A[0], A[1], ..., A[i] 第2部分:A[i+1], A[i+2], ..., A[j-1] 第3部分:A[j], A[j+1], ..., A[A.length - 1] 3つの部分はそれぞれ等しい2進数値を持つ必要があります。条件を満たす分割が存在しない場合は [-1, -1] を返します。 たとえば、入力が
-
C++で解く「異なる部分列 II」:動的計画法による効率的な数え上げアルゴリズム
問題概要文字列 S が与えられたとき、S の「異なる部分列(distinct subsequences)」の総数を求めることを考えます。ここで部分列とは、元の文字列から一部の文字を選んで並び順を保ったまま取り出した文字列のことです。答えは非常に大きな値になる可能性があるため、109 + 7 で割った余りを返します。例として、入力が bab の場合を考えてみましょう。このとき出力は 6 になります。実際に存在する異なる部分列は次の 6 つです。a、b、ba、ab、bb、abb解法のアプローチこの問題は動的計画法(DP)を使うことで、文字列の長さに対して線形時間で解くことができます。ポイントは、「