01背包


視頻地址:

https://www.bilibili.com/video/BV1U5411s7d7?

 

 

一,0-1 背包題目

給定一組物品,每種物品都有自己的重量和價格,在限定的總重量內,我們如何選擇,才能使得物品的總價格最高。其中,每件物品都只能選擇一次。

 

 

二,錯誤的思考

之前曾經想到,可以求出每件物品的單價(即 價格/重量),在根據單價進行排序和選擇。后來寫出來答案不對,才發現這樣做有個問題,即這里的物品並不是真的稱斤買的。

例如,你背包空間剩下 4,此時還剩下兩件物品沒有決策,一個 w:1,v: 5,一個 w: 4,v: 6。這時,兩件物品無法同時選擇,答案很明顯應該選第二個,但是在這種方法下,會選擇第一個,因為第一個單價更高。

這種方法會出錯的原因在於:到了最后,盡管你單價更高,但你的空間利用的不夠。

錯誤的代碼:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define N 100
struct Node
{
    int w, v;
    double av;
}kp[N];
bool vis[N];   // 標記這個物品是否偷過
bool cmp(Node a, Node b)
{
    return a.av - b.av < 1e-6;
}
int W, n;  // W 背包的最大空間  ,n 代表物品個數
int V;  // 最大價值
void knapsack_01()
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (kp[j].w < W&&vis[j] == 0)
            {
                vis[j] = 1;
                W -= kp[j].w;
                V += kp[j].v;
            }
        }
    }
}
int main(void)
{
    scanf("%d%d", &n, &W);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d", &kp[i].w, &kp[i].v);
        kp[i].av = (double)kp[i].v / (double(kp[i].w));
    }
    sort(kp + 1, kp + n + 1, cmp);
    knapsack_01();

    printf("%d\n", V);
    
    system("pause");
    return 0;
}
/*測試數據:
5 20
2 3
3 4
4 5
5 8
9 10
*/
View Code

 

 

三,算法分析

  這種 01背包是比較一般性的動態規划問題,即可以由前面的狀態堆出后面的狀態。所以可以從函數關系和函數出口概括這種問題。( 。ớ ₃ờ)ھ 

設 B(k,w) :

  如果背包共計能裝 w 重的商品,那么在只能選擇前 k 個商品的前提下,背包能裝的物品的最大的價值。

則有

  函數關系:

  在對 B(k, w) 進行分解時,可以將其分為三種情況:

    B(k, w)  =  max ①②③

      ① 為 B(k-1, w)        代表  第 k 件太重了,放不下,所以偷不了

      ② 為 B(k-1, w)        代表  第 k 件放得下,但選擇不偷

      ③ 為 B(k-1, w-wk) + vk   代表  第 k 件放得下,且很選擇偷

  函數出口:

    B(0, w) = 0  一個都不能偷,偷個寂寞

    B(k, 0)  = 0  一個都放不下,望洋興嘆

 

 

 四,代碼

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define MAX(x,y) (x>y?x:y)
#define N 100
int b[N][N];
int w[N], v[N]; // 商品重量,商品價值
int n, m;        // 物品個數,背包體積
int dp(int k, int m)
{
    if (k == 0)
        return 0;
    if (w == 0)
        return 0;
    if (w[k] > m) // 放不下
        return dp(k - 1, m);
    return MAX(dp(k - 1, m - w[k]) + v[k], dp(k - 1, m));
}
void knapack_01()
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (w[i] > j) // 放不下
                b[i][j] = b[i - 1][j];
            else
                b[i][j] = MAX(b[i - 1][j - w[i]] + v[i], b[i - 1][j]);
        }
    }
}
void show()
{
    printf("\nw == > ");
    for (int i = 0; i <= m; i++)
        printf("%3d", i);
    puts("");
    for (int i = 0; i <= n; i++)
    {
        printf("第%d件: ", i);
        for (int j = 0; j <= m; j++)
        {
            printf("%3d", b[i][j]);
        }puts("");
    }
    printf("當背包可裝重 %d 的物品時,最多可偷 %d 價值的物品\n", m, dp(n, m));
}
int main(void)
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)    // 商品下標從1開始
        scanf("%d%d", &w[i], &v[i]);

    knapack_01(); // 01 背包
    show();

    system("pause");
    return 0;
}
/*測試數據:
5 20
2 3
3 4
4 5
5 8
9 10
*/
View Code

 

 

 

========== ========= ======== ======= ====== ===== ==== === == =

  清平樂 · 六盤山    毛潤之

天高雲淡,望斷南飛雁。

不到長城非好漢,屈指行程二萬。

六盤山上高峰,紅旗漫卷西風。

今日長纓在手,何時縛住蒼龍?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM