web-dev-qa-db-ja.com

私のブルートフォースソリューションは遅すぎる、必要なDPソリューション

簡潔な問題の定義:

given n and {a,b,c}; 
(1 ≤ n, a, b, c ≤ 4000);
Constraint -> a*i + b*j + c*k==n (i,j,k>=0);
Objective-> maximize(i,j,k)

例:

n=47 and a=7,b=5,c=8 -> max=9 (i=1,j=8,k=0) == 7*1+5*8+8*0=47
n=7 and a=5,b=5,c=2  -> max=2 (i=1,j=0,k=1) or (i=0,j=1,k=1) 

私の最初の解決策はブルートフォースであり、{4000や1,2,3}などの入力に対して「TIME_LIMIT_EXCEEDED」を提供します。この問題は[〜#〜] dp [〜#〜]を使用して解決できると思いますが、理解できません。誰かがこの問題からDPソリューションを構築する方法を提供してくれれば非常にありがたいです。 (私が欲しい主なことは単なるソリューションではありませんが、DPソリューションを構築する方法を十分に理解したいです)。以下は私の解決策です:

#include <iostream>
using namespace std;

int main() {
    int n,a,b,c;
    cin>>n>>a>>b>>c;
    int max=0;

    for (int i=0;i<=n/a;i++) { 
        for (int j=0;j<=n/b;j++) { 
            for (int k=0;k<=n/c;k++) { 

                if ((a*i+b*j+c*k)==n) {
                    if (max<i+j+k) {
                        max = i+j+k;
                    }
                }

            }
        }
    }
    cout<<max;
    return 0;
}
5
Humoyun Ahmad

OPはコメントで、「ブルートフォースとDP」としてタグ付けされた問題を見つけたと述べました。これは絶対にばかげています。線形時間ソリューションは次のとおりです。

  1. A、b、cの順序はまったく関係がないので、a≥b≥cになるように並べ替えます。

  2. G = gcd(a、b、c)(最大公約数)を計算します。 nがgで割り切れない場合、解決策はありません。それ以外の場合は、a、b、c、nをgで割ります。ここで、gcd(a、b、c)= 1です。

  3. G = gcd(b、c)を計算します。これは、n-aiがgの倍数でなければならないことを意味します。 i0 = n-aiがgの倍数になるような最小のiを見つけます。このようなiは、gcd(a、g)= 1であるために存在します。解につながる可能性のあるiの可能な値は、i0、i0 + g、i0 + 2gなどです。 n --a * i0 <0の場合、解決策はありません。

  4. Bとcをgで割ります。ここで、(n-ai)/ g = bj + ckを解くj、kを見つける必要があります(i = i0、i0 + g、i0 + 2gなど)。 gcd(b、c)は1になります。これらのiのそれぞれについて、m =(n --ai)/ gとすると、bjをモジュロc = mをモジュロcにする必要があります。 gcd(b、c)= 1であるため、この方程式を解く最小のj0≥0があります。これを解く他の値jは、j0 + c、j0 + 2c、j + 3cになります。ただし、jをcだけ増やすたびに、kをbだけ減らす必要があり、b≥cであるため、これでは解を改善できません。 k =(m --b * j0)/ cがあり、このkは≥0でなければなりません。jを大きくしても、jが小さい場合にkが0未満の場合、k≥0にすることはできません。これは、特定のiに対して、b = modulo c = m modulo c、およびk =(m-b * j0)/ cとなるようなj =最小jを選択することを意味します。

  5. J0をすばやく見つけるために、モジュロ値mをbjモジュロc = mで最小のjにマッピングするルックアップテーブルTを作成します。これは、c *を法とするb * 0、cを法とするb * 1などを計算し、0≤t <cに対してT [b * t modulo c] = tを設定することにより、線形時間で行われます。

  6. アルゴリズムはi = i0から始まり、これまでのところ解決策は見つかりません。 n --ai <0の場合、これまでに見つかった最良の解で戻ります。それ以外の場合は、m =(n --ai)/ gとします。 j = T [m modulo c]、k =(m-b * j)/ cとします。 k≥0で、i + j + kがこれまでの最良の解よりも優れている場合、新しい最良の解を見つけました。それ以外の場合は、iをi + gに置き換えて続行します。

  7. Mを計算した後、i + j +k≤i+ m/cが増加しないことがわかっていることを確認することで、アルゴリズムを高速化します。したがって、解が見つかり、i + m/cがその解より大きくない場合、これ以上改善できない解が見つかったことがわかっているため、アルゴリズムを終了できます。

入力n = 4000、a、b、c = 1、2、3の場合:a = 3、b = 2、c = 1を再配置します。gcd(a、b、c)= 1 so a、b、c、 nは変更されません。 g = gcd(b、c)= 1であるため、b、cは変更されないため、テーブルTには1つの値T [0] = 0が含まれます。

I = 0とし、n-aiはg = 1で割り切れます。したがって、i = 0、m = 4000、j = T [0] = 0、k =(4000-j * b)/ c =から始めます。 4000、解はi = 0、j = 0、k = 4000になります。

次に、i = 1、m = 3997とします。i+ m/c = 3998 <4000なので、前に見つけた解が最適です。したがって、制限時間を超えた問題は実際には非常に単純なので、完全な計算を書き留めることができます。 (ところで、a、b、c、nをgcd(a、b、c)で除算した後にc = 1の場合、i = 0、j = 0、k = nが常に最適解です)。

ユークリッドのアルゴリズムを使用してi0より速いO(g)を見つけ、O(c)でテーブルTを作成する代わりに、ユークリッドのアルゴリズムを使用して値jを見つけた場合、解は時間的に劣線形になる可能性があると思います。見つかったソリューションが最適であることがすぐにわかります。

1
gnasher729

3番目のループを取ります:それは絶対に不必要です。 (a * i + b * j + c * k)== nかどうかを確認します。つまり、kは(n --a * i --b * j)/ cに等しくなければなりません。したがって、些細な変更は、この1 kを計算して、それが解であるかどうかを確認することです。

A≥b≥cになるようにa、b、cを並べ替えます。ここで、jが大きくなると、kはそれ以上縮小する必要があるため、j + kの合計は大きくなりません。つまり、iとjが与えられたときにkの値を見つけた場合、それより大きいjをわざわざチェックする必要はありません。合計i + j + kはこれ以上大きくなりません。

次に、Euclidsアルゴリズムを見て、bj + ck = n-aiの解を見つけます。そして最後に、iが大きくなるにつれてi + j + kがどれだけ大きくなるかを見積もることができます。繰り返しますが、私が大きくなると、合計は少なくなる傾向にあります。私が十分に大きい場合、合計がこれ以上大きくならないことを証明できます。

5
gnasher729

再帰の場合、初期問題f(n、a、b、c)を再定義できます。ここでai + bj + ck = nからf(n、a、b、c)= ai + f(n-ai、b、c) )ここで、f(n、a)はnの約数を見つけることになります。よろしければ、詳細を追加いたします。モバイルでの回答は困難です。

0

「DP」の質問に答えるには:これは3つのステップで行うことができます。

  1. _a*i==m_の解を見つけます。ここで_1<=m<=n_:これは簡単です。_m%a==0_かどうかを確認するだけで、解はi = m/aになります。そうでない場合、解決策はありません。これらを配列に格納します。

  2. _a*i + b*j==m_の解を見つけます。ここで_1<=m<=n_、最大化(i + j):すべてのjを_1<=j<=m/b_でループし、ステップ1の結果を使用して_a*i == m-b*j_を解きます。 、最大の合計を持つペア(i、j)を保持します。結果として合計のみが必要な場合は、合計(i + j)を配列に保持するだけで十分です。

  3. 最後に、すべてのkをループし、ステップ2を使用してa*i + b*j == n - c*k; maximize(i+j)の解を取得することにより、_a*i + b*j + c*k==n_を解きます。トリプル(i、j、k)を最大の合計で維持します。

0
Doc Brown