ダイナミックプログラミング - ナップザック問題パイソンが実装(01バックパックフルバックパック、複数のバックパック)

参考:

リュック9つのストレス - 嗶哩嗶哩

バックパック9つのストレス



01ナップザック問題

01ナップザック問題

説明:

N項目Vとバックパックの容量があります。

ボリュームは、i番目の項目のVI、値のwiです。

バックパックにアイテムを解決する何を、これらの項目の合計容量は、バックパックのトラフィック、および最大の合計値を超えることはできません。


二次元動的計画

F [i] [j]は、jは合計値が最大数であり、総体積であるi番目の項目を見て正面を表します。結果= MAX(F [N] [0〜V])F [I] [J]。

  • i番目の項目が選択されていない[I] [j]はF [I-1] [j]を= F。

  • i番目の項目から選択[i] [j]は最大テイク間F [I-1] [JV [I] + W [I](V [i]は、物品の体積i番目です)= F 。初期設定:[0] [0] 0 = F(結果的にいくら能力、状況を選択しません、0ですか?)

コードは以下の通りであります:

n, v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])

# 初始化,先全部赋值为0,这样至少体积为0或者不选任何物品的时候是满足要求  
dp = [[0 for i in range(v+1)] for j in range(n+1)]

for i in range(1, n+1):
    for j in range(1,v+1):
        dp[i][j] = dp[i-1][j]  # 第i个物品不选
        if j>=goods[i-1][0]:# 判断背包容量是不是大于第i件物品的体积
            # 在选和不选的情况中选出最大值
            dp[i][j] = max(dp[i][j], dp[i-1][j-goods[i-1][0]]+goods[i-1][1])


print(dp[-1][-1])


一次元の動的最適化

上から二次元図、F [i]が唯一[I-1]を[〜V 0]前の状態を格納するだけの一次元アレイを用いて、Fに関連付けられました。それでは、どのようにそれを達成するには?

最初の質問:状態遷移

DP状態のストレージアレイを想定し、そこであるべきです。

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

関数maxのDP [I]内の状態の値を表します。

2番目の質問:初期化

ここで選択された項目、0〜Vで最大値の量(値は全て0である)場合に一次元配列V + 1、0の長さの初期化を開始します。

3番目の質問:再発関係

私は、i番目の状態は、1次元配列の値を使用するときに打つことを確認して、想像することは最初のi-1の状態です。私は前面から背面に押し、私は戻って横断するとき、私が使用される場合、i番目の状態は状態ではなく最初のi-1状態があります。例えば:

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

どこDP [IV [i]は]再割り当てされたこと、状態ではなく、値が、これは間違っています。したがって、我々はしなければならないバックから前方に押し

n, v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])

dp = [0 for i in range(v+1)]

for i in range(n):
    for j in range(v,-1,-1): # 从后往前
        if j >= goods[i][0]:
            dp[j] = max(dp[j], dp[j-goods[i][0]] + goods[i][1])

print(dp[-1])


例体積を決定

そうでない場合、私は可能な限り最大の値を尋ねるが、容量はバックパックの最大値にちょうど等しい場合、どのようにそれを行うには?


完全なナップザック問題

完全なナップザック問題

説明:

N項目Vとバックパックの容量がありますが、各項目は無制限ています!

ボリュームは、i番目の項目のVI、値のwiです。

バックパックにアイテムを解決する何を、これらの項目の合計容量は、バックパックのトラフィック、および最大の合計値を超えることはできません。


一次元動的プログラミング

01の最大の違いとの完全なナップザック問題のナップザック問題は、各項目が何回も選択することができるということであるので、我々は考慮にi番目の記事を取るとき、私たちは、検討する必要があります、選挙を二回、この項目を選択し、この時間の項目を選択しないでください......この記事は、(選択された番号k、k個の*のV [I]> J、Jバックパック電流容量)まで選択することができない、それらの状況から最大値を選択します。コードは以下の通りであります:

n, v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])
dp = [0 for i in range(v+1)]
for i in range(n):
    for j in range(v,-1,-1): # 从后往前
        k = j//goods[i][0]  # 能选多少次
        # 从这些次里面取最大
        dp[j] = max([dp[j- x* goods[i][0]] + x * goods[i][1] for x in range(k+1)])

print(dp[-1])


一次元動的プログラミング(最適化)

ただ、その質問に、我々は01ナップザック問題、バックからの再帰的な前進を継続しています。しかし、そのことについては、実際には、再発過去で戻って。それを理解するには?

2 i番目の項目を検討する際の状態を仮定します。

A:dp[k*v[i] + x]

B:dp[(k-1)*v[i] + x]

再帰の前の状態の上に、前述の誘導、:

  • K + 1の要求値は、選択された最大の状態であるべきです。

    dp[x] + k*w[i]
    dp[v[i] + x] + (k-1)*w[i]
    dp[2*v[i] + x] + (k-2)*w[i]
    ...
    ...
    dp[(k-1)*v[i] + x] + w[i]
    dp[k*v[i] + x
  • Bの要求値がk最大の状態から選択されるべきです。

    dp[x] + (k-1)*w[i]
    dp[v[i] + x] + (k-2)*w[i]
    dp[2*v[i] + x] + (k-3)*w[i]
    ...
    ...
    dp[(k-2)*v[i] + x] + w[i]
    dp[(k-1)*v[i] + x

私たちは、一単語の上に、2つの状態は[i]の値wが1つだけactually'reことがわかります。したがって:

我々は、この時点での状態に応じて、(I-1)状態の前に導入されることができるが、一方、現在のサブ問題の電流値は、上記の状態の問題であるので、私たちは後者の値に前回値から押し込むことができます。

次のように再帰的なコードを前後です。

n, v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])
dp = [0 for i in range(v+1)]
for i in range(n):
    for j in range(v+1):
        if j >= goods[i][0]:
            dp[j] = max(dp[j], dp[j-goods[i][0]] + goods[i][1])

print(dp[-1])

複数のナップザック問題

複数のナップザック問題

説明:

N項目Vとバックパックの容量があります。

Iアイテムの体積は、VI、WIの値、数Siです。

バックパックにアイテムを解決する何を、これらの項目の合計容量は、バックパックのトラフィック、および最大の合計値を超えることはできません。


一次元動的プログラミング

上記と同様のフルナップザック問題で、実際には、我々はより前方に移動し、私はオブジェクトの数がで再考されるべきで選択すると、再帰:

k = min(s[i], j//v[i]), j为当前的背包容量

コードは以下の通りであります:

n,v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])

dp = [0 for i in range(v+1)]

for i in range(n):
    for j in range(v, -1, -1):
        # 考虑两种情况的最小值
        k = min(j//goods[i][0], goods[i][2])
        dp[j] = max([dp[j-x*goods[i][0]] + x*goods[i][1] for x in range(k+1)])

print(dp[-1])


一次元動的プログラミング(変換01バックパック)

アイデアは、拡張項目1の量の多くに発展し、これ01ナップザック問題に変換するために直接アイテムのバックパックに、簡単です。コードは以下の通りであります:

n,v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])

new_goods = []

# 展开
for i in range(n):
    for j in range(goods[i][2]):
        new_goods.append(goods[i][0:2])

goods = new_goods
n = len(goods)

# 01背包问题
dp = [0 for i in range(v+1)]

for i in range(n):
    for j in range(v,-1,-1):
        if j>= goods[i][0]:
            dp[j] = max(dp[j], dp[j - goods[i][0]] + goods[i][1])

print(dp[-1])

おすすめ

転載: www.cnblogs.com/anzhengyu/p/11408466.html