DP-ナップザック問題

ナップザック問題のテンプレート理論

バックパックのアイテムの最大の合計値を作るためにドレスアップする方法、物事を保持するために、[i]は、現在はバックパックに総容量Vとバックパック、今N財が存在し、i番目の記事能力の重量[i]は、値の値。メイン3つのカテゴリに分類:

  • 01バックパック、各物品は、値0または1をとることができます。
  • 完全なバックパックは、各項目は、回数無制限の数を取ることができます。
  • 複数のバックパックは、各アイテムは多くの制限を有し、i番目の項目は、[I] NUMをアップすることができます。

全体的に、それはちょうどなし2例で満たさバックパックで満たさバックパックに分かれています

01ナップザック問題

各項目の一つだけが持っている、とだけ選択二つの状態が選択されていない
N項目のVとバックパックの容量、各項目の一つだけが選択または残すことができますがあります。W [i]は、値V [i]があると、i番目の商品の重量。これらの項目の重みの和がバックパックの容量、および最大値の合計を超えないことができ、バックパックの中に物品を解きます。

ご質問の場合のみ2つの状態があります01ナップザック問題に変換することができます

DP [i]は[V] iの項目は正確利用可能な最大容量値Vバックパックを置いた正面を表す状態を定義しました。
状態遷移方程式:
dp[i][v]=max(dp[i-1][v], dp[i-1][v-w[i]]+v[i])

`dp[v]=max(dp[v],dp[v-w[i]]+v[i])`

空間複雑性O(N Wである)、又はO(W)は、時間計算量はO(N Wです)

for(int i=0;i<n;i++){
    for(int j=背包大小;j>=w[i];j--){
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    }
}

分析:
まず、0からnまでのサイクルは、N個のオブジェクトを指す
第二に、現在のオブジェクトの重量にバックパックサイズから循環は、時間の複雑さを軽減し、そう得ることである
場合は、[i]は、物体の重量であり、Wの[I]、及びリサイクルする必要はありませんW残りのバックパックのサイズよりも小さい場合、それはWエンドポイントは[i]は
、jはバックパックの総容量、バックパックは、容量の大きさよりも大きくないの現在の意味ので、バックパックの他の大きさでありますサイズ
が上昇した場合、それはDPの意志[JWは、[i]は】操作され、それは、jが倍数で[I] wがある場合、V [i]が完全にバックパックである、添加され続ける、ということを意味します問題
降下のために、それはすべてのj番目のオブジェクトが1つだけでこれを配置することができる時間ではなく、複数回、すなわち01バックパックであることとを保証します

複数のナップザック問題

最大n個で利用可能な各物品
商品のN種類のとV.のバックパックの容量 i番目の項目は、各ボリュームはC、Wの値であり、最もn個で入手可能です。これらの項目の容積の合計はバックパックの容量、および最大値の合計を超えないことができ、バックパックの中に物品を解きます。

複数ナップザック問題のために、そのような5の2つの値として、01ナップザック問題に変換することができ、物品2の重量は、2つのアイテムAおよびBに変換するために、5の値は、2の重量、5が、また値であります図2は、重量で

状態遷移方程式:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*w[i]]+k*v[i]) 其中0<=k<=c[i]

`dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i])`

kは置き、各項目の数であります

 for(int i=1;i<=n;i++)
    for(int j=m;j>=0;j--)
        for(int k=1;k<=c[i];k++){
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        }
    }
}

完全なナップザック問題

各項目は、無限要素を有する
物品のN種類やバックパック容量Vが存在し、各項目は無限の要素が利用可能である持っています。ボリュームは、i番目の項目Cであり、Wの値です。これは、これらの項目の容量のバックパック和への記事はバックパックの容量、および最大値の合計を超えていないことができます。

状態遷移方程式:
dp[i][j]=max(dp[i][j],dp[i-1][v-k*w[i]]+k*v[i]) 其中0<=k*w[i]<=背包大小

`dp[j] = max(dp[j], dp[j - w[i]] + v[i]);`

内側ループの循環方向は、立ち上がり異なっているように秋、01リュック解析オブジェクト、及び01バックパックを見ます

度ハイブリッドスペースO(N Wである)、又はO(W)は、時間計算量はO(N Wです)

コード

for (int i = 1; i <= n; i++) {
    for (int j = w[i]; j <= 背包大小; j++) {
        dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    }
}

バックパックミックス

唯一の最大値は、いくつかは、いくつかのアイテムの無制限選択することができる、いずれかを選択した場合にバックパックの三種類は、混合した
裁判官、01バックパックと完全に異なるコードのバックパックラインの使用

for(int i=0;i<n;i++){
    if(第i个物品是01背包){
        for(int j=背包大小;j>=w[i];j--){
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        }
    }else if(第i个物品是完全背包){
        for (int j = w[i]; j <= 背包大小; j++) {
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }
    }
}

あなたは、複数の単語リュックサック追加した場合
の擬似コードのバックパックミックスを

for i= 1 to n
    if 第i件物品属于01背包
        ZeroOnePack(dp,wi,vi)
    else if 第i件物品属于完全背包
        CompletePack(dp,wi,vi)
    else if 第i件物品属于多重背包
        MultiplePack(dp,wi,vi,ni)

概要テンプレート

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=1000000;
int v[N],w[N];
int dp[N];
void ZeroOnePack(int i){
    for(int j=背包大小;j>=w[i];j--){
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    }
}
void CompletePack(int i){
    for(int j=w[i];j<=背包大小;j++){
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    }
}
void MultiplePack(int i){
    for(int j=m;j>=0;j--)
        for(int k=1;k<=c[i];k++){
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        }
    }
}
int main(){
    memste(dp,0,sizeof(dp));
    for(int i=0;i<n;i++){
    if(第i个物品是01背包问题){
        ZeroOnePack(i);
    }else if(第i个物品是完全背包问题){
        CompletePack(i);
    }else if(第i个物品是多重背包问题){
        MultiplePack(i);
    }
}

タイトルレコードを行います

最初の質問:

完全なナップザック問題
HUD1248

問題の説明
アンデッドリッチキングの賃金はNデスナイトは、ドル札を(、唯一の法案を覚えている)を取得、頻繁に自分自身を防ぐ戦いで死ぬために、プル、彼は彼自身の小道具のいくつかを購入することを決めました彼はゴブリン商人に来ました。

デスナイト:「私は小道具を購入したいです!」

ゴブリン商人:「ここでは3つの小道具、ポーション150、200魔法の薬、ポーション350無敵を持っています。」

デスナイト:「オーケー、私にポーションを与えます。」

その後、彼は左利きドル紙幣を取り出したNゴブリン商人が行きます。

ゴブリン商人:「私はあなたを思い出させるために忘れて、私たちは、ゲストのお金を探しているの習慣、そして我々はちょっと、先端を受けているより多くのお金を持っていません。」

デスナイト:「......」

デスナイトは後で購入するために、とにかく、彼自身が同様に多くの小道具を買う可能性がある場合、それは先端お金を送るだろうと思った、自宅で朝食を買ったり、できるだけ少ないが、彼はヒントを獲得するために。

死の騎士は今、彼は少なくとも先端実業家ゴブリンを与えるどのくらい、あなたは彼が計算助けたいと思います。

入力
、入力データの最初の行は整数T(1 <= T <=であり 、100) 、テストデータの数を表す試験データのT行、テストデータは、各だけ正の整数N(1 <= Nを<含有されています= 10000)、Nは、銀行券の手の中に死の騎士の額面を表します。

注意:ゴブリン店は、タイトルに記述わずか3項目です。

出力
各テストケースの場合は、あなたは先端の細いとして商人にどのくらいのお金最小出力死の騎士を無駄にする必要があります。

サンプル入力

2
900
250

サンプル出力

0
50

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int w[]={150,200,350};
int v[]={150,200,350};
int dp[10005];
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));

        for(int i=0;i<3;i++){
            for(int j=w[i];j<=n;j++){
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
            }
        }
        printf("%d\n",n-dp[n]);
    }

    return 0;
}

2番目の質問:

完全背包问题
問題の説明:
ACMは何かを行うことができます前に、予算を準備し、必要な財政支援を得なければなりません。このアクションの主な収入は、不可逆的に結合したマネー(IBM)から来ています。背後にある考え方は単純です。いくつかのACMのメンバーはどんな小さなお金を持っているときはいつでも、彼はすべてのコインを取り、ピギーバンクにそれらをスローします。あなたは、このプロセスは不可逆的である、コインが豚を壊すことなく取り外すことができないことを知っています。十分に長い時間の後、支払われる必要があるすべてのものを支払うためにピギーバンクに十分な現金があるはずです。

しかし、ピギー銀行との大きな問題があります。内部でどのくらいのお金を決定することはできません。だから我々は唯一の十分なお金がないことを見つけるために粉々に豚を壊すかもしれません。明らかに、我々はこの不愉快な状況を回避したいです。唯一の可能性はピギーバンクを計量し、内部のどのように多くのコインを推測しようとすることです。我々は正確に豚の重量を決定することができることを、我々は特定の通貨のすべての硬貨の重みを知っていると仮定します。そして、お金のいくつかの最小量は、我々が保証できるピギーバンクにあります。あなたの仕事は、この最悪のケースを見つけるとピギーバンク内の現金の最小量を決定することです。君の力が必要なんだ。これ以上早まっ豚を破りません!
入力
入力は、Tテストケースから構成されています。それらの数(T)は、入力ファイルの最初の行に与えられています。各テストケースは、EおよびFは、それらが空豚のとコインで満たされた豚の体重を示す2つの整数を含む行から始まります。どちらの重みはグラム単位で与えられています。いいえ豚は1 <= E <= F <各テストケースの2行目に= 10000、数を与える整数N(1 <= Nが<= 500)が存在することを意味し、10kg以上の重量を量るません所与の通貨に使用される様々なコインの。これに続いてNライン、各指定ワンコインタイプが正確です。これらのラインは、二つの整数それぞれ、PAND Wを(1 <= P <= 50000、1 <= W <= 10000)を含有します。Pは、貨幣単位でコインの値であり、Wは、グラム重量だです。
出力
各テストケースのために、正確に出力の1行を印刷します。行は、文が含まれている必要があり、「ピギーバンクにお金の最小量をXです。」Xは、与えられた総重量とコインを使用して達成することができるお金の最小量です。重量は正確に到達することができない場合は、「これは不可能です。」行を印刷します。
サンプル入力

3
10 110
2
11
30 50

10 110
2
11
50 30

1 6
2
10 3
20 4

サンプル出力

ピギー銀行にお金の最小量は60です
ピギー銀行にお金の最小量は100です
。これは不可能です。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const long long INF=500005,N=10005;
int w[N];
int v[N];
int dp[N];
int main(){
    int t;
    cin>>t;
    while(t--){
        int e,f;
        scanf("%d%d",&e,&f);
        int n;
        cin>>n;
        int W=f-e;
        for(int i=0;i<n;i++){
            scanf("%d%d",&v[i],&w[i]);
        }
        //求最小值,所以对所有的dp进行赋值大数字,但除了第一个数,因为对所有赋值的话,最小值就是大数
        for(int i=1;i<=W;i++){
            dp[i]=INF;
        }

        for(int i=0;i<n;i++){
            for(int j=w[i];j<=W;j++){
                dp[j]=min(dp[j],dp[j-w[i]]+v[i]);
            }
        }

        if(dp[W]==INF){
            printf("This is impossible.\n");
        }else{
            printf("The minimum amount of money in the piggy-bank is %d.\n",dp[W]);
        }

    }

    return 0;
}

3番目の質問:

01ナップザック問題のDP確率
HDU1203
問題は説明
Speakless長い時間が私は海外に行きたかった、そして今、彼が準備するすべての材料を準備し、必要なすべての試験を終えたので、彼らは学校のために適用する必要があります。海外の任意の大学のために適用するには、あなたが適用する料金を支払う必要があり、これは非常に印象的です。Speaklessはたったの$ n個万ドルの合計を保存するために、多くのお金を持っていません。彼は、m個の学校で(彼の手頃な価格の範囲内で当然の)の数を選択します。各学校には、異なるアプリケーション料(百万ドル)を有し、Speaklessは、彼は学校がBの可能性を提供し得たと推定しました。オファーは別の学校間でお互いに影響を与えないかどうか。「私はオファーをNEED」、と彼は叫びました。貧しい人々を助け、彼は彼が少なくとも一つのオファーの最大確率を受けることができることを計算するのに役立ちます。(複数Speaklessの学校を選択した場合は、任意のオファーの学校ができ得ます)。

入力
データの入力いくつかのグループであり、最初の2つの正の整数の各データ線N-、M(0 <= N - <= 10000,0 <= M <= 10000)
の行の後ろにmは、各ラインは、二つを有していますデータAI(整数)、BI(リアル)、i番目の学校の申請料を意味し、確率の提供を得ることができます。
最後に、2つの0入力があります。

出力は、
それぞれ少なくとも一つのオファーの最大確率を得ることができるSpeaklessを表すデータ出力に対応します。彼は、最寄りの小数点第2位を、パーセンテージで表しました。

サンプル入力

10 3
4 0.1
4 0.2
5 0.3
0 0

サンプル出力

44.0パーセント

ヒント

あなたは「%」を印刷するのprintf(「%%」)を使用する必要があります。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100000+5;
int v[N];
float p[N],dp[N];
int main(){
    int n,m;//背包大小和学校数目
    while(scanf("%d%d",&n,&m),(n+m)!=0){
        for(int i=0;i<m;i++){
            scanf("%d%f",&v[i],&p[i]);
        }

        memset(dp,0,sizeof(dp));

        for(int i=0;i<m;i++){
            for(int j=n;j>=v[i];j--){
                dp[j]=max(dp[j],1-(1-dp[j-v[i]])*(1-p[i]));
            }
        }


        printf("%.1f%%\n",dp[n]*100);

    }
    return 0;
}

第四問:複数のナップザック問題

複数ナップザック問題HDU2191、この方法は、時間複雑さO(NCV)であります

問題の説明は、
今、あなたは、n、m、および米の市場一種の総資本があると、米の各袋は、製品、その価格帯であり、唯一の全体の袋を購入することができます。
私が尋ねる:あなたはどのように多くの穀物のキロはそれを購入するまでの限られた資金を使用していますか?

入力
データは、第一の正の整数Cを含んでいる、グループCは試験を発現し、各テストの最初の行は、2つの整数n及びmは(1 <= N <= 100、1 <= M <= 100) 量と米の種類は、資金、次いで、m行を表す3つの数字を含む各列P、H、及びC(1 <= p <= 20,1 <= hの<= 200,1 <= C <= 20)、それぞれ、袋、重量の価格と米の種類に応じた袋の袋の数。

出力
試験データの各セットについては、重量が出力米を買うために、あなただけの資金の全てが米を購入することを想定することはできません、と資金は、あなたが終了することはできません。ライン当たり各インスタンスの出力。

サンプル入力
1
8 2
2 100 4
4 100 2

サンプル出力
400

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){

        int n,m;
        scanf("%d%d",&n,&m);

        int w[105],v[105],c[105];
        int dp[205];

        memset(dp,0,sizeof(dp));

        for(int i=0;i<m;i++){
            scanf("%d%d%d",&v[i],&w[i],&c[i]);
        }

        for(int i=0;i<m;i++){
            for(int k=1;k<=c[i];k++){
                for(int j=n;j>=v[i];j--){
                   dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
                }
            }
        }
        printf("%d\n",dp[n]);

    }


    return 0;
}

最適化

バイナリ最適化された時ではないでしょう。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int w[105],v[105],c[105];
int dp[205];//dp[i]指的是购买价值为i的大米时的总重量

//v为当前的价值,w为当前的重量
void zeroonebag(int v,int w){
    for(int j=n;j>=v;j--){
        dp[j]=max(dp[j],dp[j-v]+w);
    }
}
void completebag(int v,int w){
    for(int j=v;j<=n;j++){
        dp[j]=max(dp[j],dp[j-v]+w);
    }
}
void multiple(int v,int w,int c){
    //价值大于拥有的价值,则是完全背包问题
    if(c*v>=n){
        completebag(v,w);
        return;
    }
    
    //01背包
    int k=1;

    while(k<=c){
        zeroonebag(k*v,k*w);
        c=c-k;
        k=k*2;
    }

    zeroonebag(c*v,c*w);

}
int main(){
    int t;
    cin>>t;
    while(t--){


        scanf("%d%d",&n,&m);

        memset(dp,0,sizeof(dp));

        for(int i=0;i<m;i++){
            scanf("%d%d%d",&v[i],&w[i],&c[i]);
        }


        for(int i=0;i<m;i++){
            multiple(v[i],w[i],c[i]);
        }

        printf("%d\n",dp[n]);


    }

    return 0;
}

おすすめ

転載: www.cnblogs.com/Emcikem/p/11333818.html