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

C++で材料を無駄なく使い切るハンバーガーの個数を求める方法

問題の概要

2つの整数 tomatoSlices(トマトのスライス数)と cheeseSlices(チーズのスライス数)が与えられます。これらは、次の2種類のバーガーを作るための材料です。

  • ジャンボバーガー: トマトのスライス4枚とチーズのスライス1枚を使用
  • スモールバーガー: トマトのスライス2枚とチーズのスライス1枚を使用

与えられた材料をすべて使い切り、トマトのスライスとチーズのスライスの残りがどちらも0になるように、ジャンボバーガーとスモールバーガーの個数 [total_jumbo, total_small] を求めます。もし材料を余らせずに使い切ることが不可能な場合は、空の配列 [] を返します。

例として、tomatoSlices = 16、cheeseSlices = 7 が入力された場合、出力は [1, 6] になります。これは、ジャンボバーガー1個とスモールバーガー6個を作ると、トマトのスライスが 4×1 + 2×6 = 16 枚、チーズのスライスが 1 + 6 = 7 枚となり、すべての材料をちょうど使い切れることを示しています。

解法のアプローチ

この問題は、連立一次方程式として捉えることができます。ジャンボバーガーの個数を j、スモールバーガーの個数を s とすると、次の2式が成り立ちます。

  • 4j + 2s = tomatoSlices
  • j + s = cheeseSlices

この方程式を解くために、以下の手順に従います。

  1. 結果を格納するための配列 ans を用意します。
  2. トマトのスライス数が奇数、または cheese > tomato/2、または tomato > 4×cheese のいずれかに該当する場合は、材料をちょうど使い切ることが不可能なため、空の ans をそのまま返します。
  3. x := (4 × cheese − tomato) / 2 を計算します(スモールバーガーの個数)。
  4. y := (tomato − 2×x) / 4 を計算します(ジャンボバーガーの個数)。
  5. y、x の順に ans に挿入します。
  6. ans を返します。

条件チェックの理由

なぜこれらの条件で不可能性を判定できるのでしょうか。理由は次のとおりです。

  • 必要なトマトのスライス数は 4j + 2s = 2(2j + s) と表せるため、必ず偶数でなければなりません。
  • トマトの必要枚数の半分は 2j + s、チーズの必要枚数は j + s なので、cheese ≤ tomato/2 が成立する必要があります。
  • トマトのスライス数は最大でも 4(j + s) = 4×cheese 枚であるため、tomato ≤ 4×cheese が成立する必要があります。

これらの条件をすべて満たしていれば、解は必ず一意に存在することが保証されます。

C++による実装例

それでは、理解を深めるために実際の実装を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<auto> v){
   cout << "[";
   for(int i = 0; i<v.size(); i++){
      cout << v[i] << ", ";
   }
   cout << "]"<<endl;
}
class Solution {
   public:
   vector<int> numOfBurgers(int t, int c) {
      vector <int> ans;
      if(t % 2 != 0 || c > t/2 || t > c*4)return ans;
      int x = (4 * c - t) / 2;
      int y = ( t - (2 * x) )/ 4;
      ans.push_back(y);
      ans.push_back(x);
      return ans;
   }
};
main(){
   Solution ob;
   print_vector(ob.numOfBurgers(16,7));
}

入力

16
7

出力

[1, 6]

このように、連立方程式を立てて条件判定を行うことで、与えられた材料から無駄なく作れるバーガーの組み合わせを、O(1) の計算量で効率的に求めることができます。

  1. C++で数値Nが基数b表記において1から始まるかどうかを判定する方法

    本記事では、数値Nと基数bが与えられたとき、「Nを基数bで表記すると先頭の桁が1になるかどうか」を判定する方法をC++で解説します。 まず具体例を見てみましょう。数値6を2進数で表すと「110」であり、先頭は1です。4進数で表すと「12(4)」となり、これも先頭が1です。このように、同じ数でも基数によって表記が変わり、先頭の桁が1になる場合とならない場合があります。 判定の考え方(数学的な背景) 数値Nを基数bで表すと、m+1桁の数列 dm dm-1 … d0 になり、次の関係が成り立ちます。 dm × bm + dm-1 × bm-1 + … + d0 × b0 = N ここで、先頭の桁

  2. 【C++入門】変数と実数を使って配列を初期化する方法を徹底解説

    配列とは配列とは、連続したメモリ領域上に同じ型の要素をまとめて格納したデータ構造です。配列内で最も小さいアドレスは先頭の要素に対応し、最も大きいアドレスは末尾の要素に対応します。また、配列のインデックス(添字)は 0 から始まり、最大で「配列サイズ - 1」までとなります。C++では、配列を初期化する際に変数だけでなく、実数(数値リテラル)を直接指定することもできます。以下のサンプルプログラムで、その具体的な方法を見ていきましょう。サンプルコード#include <iostream>using namespace std;int main() {