完全なナップザック問題(動的計画法)

完全なナップザック問題

トピック:

N商品の種類及びVのバックパックの容量があり、各項目は無限の要素が利用可能である持っています商品のコストは、i番目のC [i]は、値[i]はwがあります。これらのアイテムの総コストは、バックパックの容量、および最大値の合計を超えないことができ、バックパックにアイテムを解きます。

 

 

まず、ダイナミックソルバーの実現可能性を検証します

 

この問題は、それを解決するために、動的プログラミングを使用して01ナップザック問題と同じにすることはできませんか?あなたができることを証明するために。

まず、矛盾によって、最適性の証明の第一の原則:

フロント項目はIに、(n1は、アイテムの第1、第2の選択数を表すN 2)完全なリュック溶液がF(N1、N2、...、NN)であり、全体サブ問題バックパックであると仮定するとTバックパックの容量と最大値を達成するため、対応する溶液である:F(N1、N2、...、NI)、溶液を準最適解ではないと仮定すると、すなわち、溶液F(M1の別のセットは、ありますM2、...、MI)、F(M1、M2、その後、F(M1、M2、...、MI)> F(N1、N2、...、NI)作り...、MI、。 ...、nnは)Fよりも大きい(N1、N2、...、NN)、したがって、F(N1、N2、...、nnは)元の問題の最適解ではない、帰無仮説と矛盾し、F(N1でなければなりません、n2は、...、NI)問題への次善のソリューションでなければなりません。

ノー後の効果を見てみましょう:

サブのいずれかの問題解決のために、解決策次のサブ問題は影響しません、私は長い間変わらず、最終的なバッグスペースなどとして、選択する前に商品の種類は、それは背後にある項目の選択に影響を与えないだろうか、です。その出会いがない後の効果。

したがって、完全なナップザック問題は、動的計画法を用いて解くことができます。

 

第二に、基本的な考え方

 

この問題は、違いは、各アイテムが無限大部分を有することで、01ナップザック問題に非常に類似しています。そのポリシーに関連付けられている、考慮の各項目の観点から、それがあることを取るかどうか二種類を取るが、0を取らなければならない、いずれかを取る、両者を取ることはなかった......と非常に多くあります。とき01バックパックに従いソリューションあなたはまだそう、考えている場合は  、[V] [i]はfを私はちょうどバックパックvの最大重量容量を置く前に商品の種類を表します。私はまだ項目ごとに異なる戦略に従って、状態遷移方程式を書くことができます。

状態遷移方程式:

あるこの01ナップザック問題  O(VN)  の状態が解決する必要があるが、各状態は、溶液時間は一定ではなくており、ステータス解決   [V] [I] Fを  時間の  、全体的な複雑さを考慮することができる  市比較的大きいです。

 

基本的なコードは次のとおりです。


#include <iostream>
#include <algorithm>
#define N 1002
using namespace std;

int f[N][N];
int w[N];
int v[N];

int main() {
    int n,W; cin >> n >> W;
    for(int i=1;i<=n;i++) {
        cin >> w[i] >> v[i];
    }
    for ( int i = 1; i <= n; i++ ) {
        for ( int j = 0; j <= W; j ++) {
            for (int k = 0; k*w <= j; k++) {
                f[i][j] = max(f[i-1][j],f[i-1][j-w[i]*k] + v[i]*k);
            }
        }
    }
    cout << f[n][W] <<endl;
    return 0;
}

 

状態遷移方程式:同じフルナップザック問題と01ナップザック問題は、空間的複雑θ(最適にするN。)1。

次のように空間的複雑後にコードを最適化します。


#include <iostream>
#include <algorithm>
#define N 1002
using namespace std;

int f[N];
int w[N];
int v[N];

int main() {
    int n,W; cin >> n >> W;
    for(int i=1;i<=n;i++) {
        cin >> w[i] >> v[i];
    }
    for ( int i = 1; i <= n; i++ ) {
        for ( int j = W; j >= 0; j --) {
            for (int k = 0; k*w <= j; k++) {
                f[j] = max(f[j],f[j-w[i]*k] + v[i]*k);
            }
        }
    }
    cout << f[W] <<endl;
    return 0;
}
 
 

第二に、シンプルかつ効果的な最適化

完全なナップザック問題は、非常にシンプルで効果的な最適化を持っているこれです:もし2つのオブジェクトがI、J会う   と   、J項目は関係なく、削除されます。この最適化は明らかに正確である:いかなる状況においても、安価な私に置き換え小さな値jの高コストは、少なくとも悪化し解決策を得ることができません。ランダムに生成されたデータのために、この方法は非常にアイテムの数、したがってその速度を減少させる傾向があります。この最適化は、単にでき  θ(N 2 )は、一般的に耐えることができ、達成します。

しかし、これはデータの項目の特別な設計があるかもしれないので、本当に頑固なことができ、最悪の場合の複雑さを改善しません。また、ナップザック問題のために、非常に良い方法である:次いで、可能な最高値で同じ項目のコストを計算するアプローチの同様の並べ替えを使用してカウントまず、Vが除去より大きい商品のコスト、  θ(V + Nの最適化を完了させます。

 

翻訳第三に、01ナップザック問題解決 

01ナップザック問題は、最も基本的なナップザック問題であるので、我々は解決するためのナップザック問題への完全な01ナップザック問題を考えることができます。最も単純な考えは、にi番目の選択した項目をあきらめている   V / C [i]を   使用すると、i番目にアイテムを置くことができるので、作品   V / C [i]は  、ピースコストと値が同じ項目で、その後これを解決します01ナップザック問題。

これは、基本的な考え方の複雑さを向上させるが、すべての後、私たちにナップザック問題の考え方に完全な01ナップザック問題与えませんでした。記事が複数の項目に分割されますが。

より効率的な変換方法であって、コストの項目にi番目の分割   の値が   複数の項目、前記Kを満たします    。これには、最適な政策オプションにi番目の記事のいくつかの作品を重要で、それは全体の数として表現できないので、バイナリ思考で  アイテムと。したがって、記事のそれぞれがに分割され     項目、大きな改善です。

 
 

四、O(VN)アルゴリズム

このアルゴリズムは、擬似コードで一次元アレイ、外観を使用しています。

for i=1..N
    for v=0..V
        f[v]=max{f[v],f[v-cost]+weight}
 

あなたは、この擬似コードとことがわかります  01擬似コードナップザック問題だけ巡回順序vは異なっています。

なぜ、このような変更はOK、それすることができますか?まず、サイクルを逆にするV = V..0に従い、なぜ01ナップザック問題を考えます。

i番目のサイクルは、状態Fの[I] [V]状態Fから[I-1] [VC [I]再帰的であることを確実にするためにするためです。言い換えれば、これが唯一の選挙に一度、各項目は、Fサブ項目のi番目の結果に選出された「オプトインI-アイテム」この戦略はありませんメンバーに基づいていますを考慮してのことを確認することを確認することです[ I-1] [VC [I]]。

そして、するとき、各項目は、オプションの無制限の部品今完全にバックパック、正確いただけますので、このようなA戦略「に加えて、i番目の商品を選ぶ」が、それは、i番目のサブ項目の結果Fに選出されたかもしれないが必要である考えます[i]の[VC [I]]、およびので、我々は、V = 0..Vループ配列でなければなりません。この簡単な手順が真実を確立理由です。

 

2つのサイクルのために上記擬似コードシーケンスを逆にしてもよいことを言及する価値があります。この結論は、最適化アルゴリズムの時定数に持参する可能性があります。

このアルゴリズムは、他のアイデアに描画することができます。明示的に、例えば、元の式に代入Fを解決するための基本的な考え方[I] [VC [I]アウト状態遷移方程式を書き込み、式はこの形式の等価な形態になることがあります。

f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}

この式は意味を表します:

問題のある子のみ(数を置く)は、i-項目の戦略を検討している場合、それは問題に変換することができ、「私はバックパックの容量をV前の項目には入れ」私だけ前にアイテムが含まれます。

あなたは私のアイテムを保持する場合、問題は、Fの「コンテナはバックパックVにアイテムを入れているI-1の前」の値に変換され、[I-1] [V]。

あなたが最初のiのアイテムを入れる場合、問題は私の項目の前に」に変換され、残りの部分に(これはここでアイテムが無限の作品であることを理由にはなりませんI-1の前に、おそらく既に最初のiの項目を入れていました) VCの容量[i]はバックパック」、バックパックの最大値は、この時点で得られるF [i]の値プラスiがアイテムを取得したWの値を確定する[VC [I] [I]であります。

コード:


#include <iostream>
#include <algorithm>
#define N 1002
using namespace std;

int f[N];
int w[N];
int v[N];

int main() {
    int n,W; cin >> n >> W;
    for(int i=1;i<=n;i++) {
        cin >> w[i] >> v[i];
    }
    for ( int i = 1; i <= n; i++ ) {
        for ( int j = w[i]; j <= W; j ++) {
            f[j] = max(f[j],f[j-w[i]] + v[i]);
        }
    }
    cout << f[W] <<endl;
    return 0;
}

 

最後に、抽象擬似コードプロセスの完全なプロセスリュックの記事:

procedure CompletePack(cost,weight)
    for v=cost..V
        f[v]=max{f[v],f[v-c[i]]+w[i]}
 
公開された118元の記事 ウォン称賛63 ビュー40000 +

おすすめ

転載: blog.csdn.net/qq_42185999/article/details/104499556