1. 程式人生 > >藍精靈算法進階——動態規劃背包(1)

藍精靈算法進階——動態規劃背包(1)

close span 數組 char font == nbsp 最大 view

我覺得我還是分好幾篇寫這個東西吧-嗷;

PACK

還有一個網站背包模板都有;AcW

1.01背包

N 件物品和一個容量是 V 的背包。每件物品只能使用一次。

i 件物品的體積是 vi,價值是 wi

求解將哪些物品裝入背包,可使這些物品的總體積不超過背包容量,且總價值最大。
輸出最大價值。

思路:用已經處理好的物品作為dp的階段,每種物品僅有一件,可以選擇放或不放。

即f[i][v]表示前i件物品恰放入一個容量為v的背包可以獲得的最大價值。則其狀態轉移方程便是:

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

但還可以進一步優化我們可以省去f數組的第一維,只用一維數組,f[j]表示外層循環到i時,已經裝了總體積為j的物品的最大價值和;

註意j到倒序循環,這樣可以保證每個物品只被選擇一次。

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
#define N 500010
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch==-)  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=x*10+ch-0;  ch=getchar();}
    x
*=f; } int n,v,f[N],c[N],w[N]; int main() { read(n);read(v); for(int i=1;i<=n;i++) read(c[i]),read(w[i]); for(int i=1;i<=n;i++) for(int j=v;j>=c[i];j--) f[j]=max(f[j],f[j-c[i]]+w[i]); cout<<f[v]<<endl; return 0; }
View Code

當然順帶寫一下另一種形式,01背包統計方案數;

CH 5201

n為物品,m為題解,這裏我們的f[j]表示和為j的方案數,max改為求和;

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
const int maxn=1100;
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while (!isdigit(ch) && ch^-) ch=getchar();
    if (ch==-) f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
int a[maxn],f[maxn];
int main()
{
    int n,m;
    read(n);read(m);
    for (int i=1;i<=n;++i)
        read(a[i]);
    f[0]=1;
    for (int i=1;i<=n;++i)
        for (int j=m;j>=a[i];--j)
            f[j]+=f[j-a[i]];
    printf("%d\n",f[m]);
    return 0;
}
View Code

2.完全背包

和01背包不同,這裏的物品有無數件,當然可以考慮二維去實現,但是像01背包一樣可以進行優化,當我們采用正序循環,對應每個物品可以選擇無數次,

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
#define N 500010
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch==-)  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=x*10+ch-0;  ch=getchar();}
    x*=f;
}
int n,v,f[N],c[N],w[N];
int main()
{
    read(n);read(v);
    for(int i=1;i<=n;i++)
        read(c[i]),read(w[i]);
    for(int i=1;i<=n;i++)
        for(int j=c[i];j<=v;j++)
            f[j]=max(f[j],f[j-c[i]]+w[i]);
    cout<<f[v]<<endl;
    return 0;
}
View Code

統計方案數:

CH 5202

我們在完全背包的基礎上將max改為求和就可以了;

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
const int maxn=4010;
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while (!isdigit(ch) && ch^-) ch=getchar();
    if (ch==-) f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
int f[maxn];
int main()
{
    int n;read(n);
    f[0]=1;
    for (int i=1;i<=n;++i)
        for (int j=i;j<=n;++j)
            f[j]=(f[j]+f[j-i])%2147483648u;
    printf("%d\n",f[n]>0?f[n]-1:2147483647);
    return 0;
}
View Code

3.多個體積維度的01背包;

POJ 1015

CH POJ 1015

藍精靈算法進階——動態規劃背包(1)