揹包問題

  揹包問題是動態規劃的基礎,也是最易理解的動態規劃,有著名的揹包九講能夠查詢,我就稍微寫一下。數組

  1. 01揹包
優化

  這個是最基礎的揹包,即這個物品只有一件,只需肯定放仍是不放便可。spa

  二維狀態轉移方程:f[i,x]=max{f[i-1,x],f[i-1,x-c[j]]+w[j]}blog

  一維狀態轉移方程:f[x]=max{f[x],f[x-c[j]]+w[j]}string

  須要注意的是,使用一維時,枚舉x須要逆序,這樣才能夠保證f[x]不受先後影響,二維則隨意。io

  同時,咱們還須要注意題目,若題目要求剛好裝滿揹包,則初始化f[0]=0,其餘爲負無窮;若沒要求剛好裝滿,則所有初始化爲0。模板

  hdu2602爲模板題。class

#include <cstdio>
#include <cstring>
#define FOR(i,x,y) for(int i=x;i<=y;++i)
#define rFOR(i,x,y) for(int i=x;i>=y;--i)
int f[1010],t,v,n,a[1010][2];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(f,0,sizeof(f));//數組初始化
        scanf("%d%d",&n,&v);
        FOR(i,0,n-1) scanf("%d",&a[i][0]);
        FOR(i,0,n-1) scanf("%d",&a[i][1]);
        FOR(i,0,n-1) rFOR(x,v,a[i][1]) if(f[x]<f[x-a[i][1]]+a[i][0])
            f[x]=f[x-a[i][1]]+a[i][0];//DP方程
        printf("%d\n",f[v]);
    }
    return 0;
}

 

  2. 徹底揹包基礎

  與01揹包的惟一區別是,每一個物品能夠無限取。循環

  二維狀態轉移方程:f[i,x]=max{f[i-1,x],f[i,x-c[j]]+w[j]}

  一維狀態轉移方程:f[x]=max{f[x],f[x-c[j]]+w[j]}

  能夠發現,二維狀態轉移方程與01揹包的有所不一樣,由於每一個物品是能夠無限獲取的;而一維的狀態轉移方程徹底相同,咱們須要作的只是改一下循環的順序,01揹包中使用逆序而徹底揹包中使用順序,就能夠不斷使用這個物品。

  另外,當數據隨機時能夠考慮一個簡單的優化,即刪去單價小的物品。

  hdu1114爲模板題。

 

#include <cstdio>
#include <cstring>
#define FOR(i,x,y) for(int i=x;i<=y;++i)
#define rFOR(i,x,y) for(int i=x;i>=y;--i)
#define MIN(a,b) a>b?b:a
int t,k1,k2,n,m,f[10010],c[510],w[510];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(f,0x3f,sizeof(f));
        f[0]=0;
        scanf("%d%d",&k1,&k2);
        m=k2-k1;
        scanf("%d",&n);
        FOR(i,0,n-1) scanf("%d%d",&w[i],&c[i]);
        FOR(i,0,n-1) FOR(x,c[i],m) f[x]=MIN(f[x],f[x-c[i]]+w[i]);
        if(f[m]<0x3f3f3f3f) printf("The minimum amount of money in the piggy-bank is %d.\n",f[m]);
        else printf("This is impossible.\n");
    }
    return 0;
}

 

 

  3. 多重揹包

  多重揹包和01揹包類似,都是有限揹包問題。

  時間要求能夠達到的狀況下O(VNK)的複雜度就能夠了,另外有一種優化方法。利用二進制的思想,把M的Weight拆分紅1,2...,2^(k-1),M-2^k+1,而後利用01揹包的方法便可,此時複雜度爲O(VNlogK).

  hdu2191爲模板題。

#include <cstdio>
#include <cstring>
#define FOR(i,x,y) for(int i=x;i<=y;++i)
#define rFOR(i,x,y) for(int i=x;i>=y;--i)
#define MAX(a,b) a>b?a:b
int t,n,m,f[110],w[110],c[110],k[110];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(f,0,sizeof(f));
        scanf("%d%d",&m,&n);
        FOR(i,0,n-1) scanf("%d%d%d",&w[i],&c[i],&k[i]);
        FOR(i,0,n-1) rFOR(x,m,w[i]) FOR(j,1,k[i]) if(x>=w[i]*j)
            f[x]=MAX(f[x],f[x-w[i]*j]+c[i]*j);
        printf("%d\n",f[m]);
    }
    return 0;
}

 

  4. 混合三種揹包

  經過以上介紹能夠發現,01揹包就是多重揹包的一種特例,所以能夠歸爲一類。咱們只需分類處理各類揹包,就能夠得到最終的答案。

  要注意的是,當某物品數量比揹包容量還要大時,咱們能夠把多重揹包轉化爲徹底揹包,這是一個很大的優化。

  下面給出hdu2844,即用到了二進制優化和揹包轉化的優化。

#include <cstdio>
#include <cstring>
#define FOR(i,x,y) for(int i=x;i<=y;++i)
#define rFOR(i,x,y) for(int i=x;i>=y;--i)
int n,m,c[110],k[110],f[100010],ans,temp,l;
int main()
{
    while(scanf("%d%d",&n,&m))
    {
        memset(f,0,sizeof(f));
        f[0]=1;
        ans=0;
        if(n==0&&m==0) break;
        FOR(i,0,n-1) scanf("%d",&c[i]);
        FOR(i,0,n-1) scanf("%d",&k[i]);
        FOR(i,0,n-1) if(k[i]*c[i]>=m)
        {
            FOR(x,c[i],m) f[x]=f[x]||f[x-c[i]];//徹底揹包。
        }else
        {
            temp=1; l=k[i];
            while(temp<=l)//二進制優化。
            {
                rFOR(x,m,c[i]*temp) if(x>=c[i]*temp) f[x]=f[x]||f[x-c[i]*temp];
                l-=temp;
                temp<<=1;
            }
            if(l) rFOR(x,m,c[i]*temp) if(x>=c[i]*temp) f[x]=f[x]||f[x-c[i]*l];
        }
        FOR(x,1,m) if(f[x]) ++ans;
        printf("%d\n",ans);
    }
    return 0;
}
//多重揹包問題,用到了二進制優化,同時還能夠把一些狀況轉化成徹底揹包來優化。

 

  5. 二維揹包

  二維揹包只是多了一個限制條件,作法與其餘揹包沒有什麼很大的區別。

  hdu2159爲模板題。

#include <cstdio>
#include <cstring>
#define FOR(i,x,y) for(int i=x;i<=y;++i)
#define MAX(a,b) a>b?a:b
int n,m1,m2,v,f[110][110],c[110],w[110];
bool bo;
int main()
{
    while(scanf("%d%d%d%d",&v,&m1,&n,&m2)!=EOF)
    {
        memset(f,0,sizeof(f));
        bo=true;
        FOR(i,0,n-1) scanf("%d%d",&c[i],&w[i]);
        FOR(i,0,n-1) FOR(x,w[i],m1) FOR(y,1,m2)
            f[x][y]=MAX(f[x][y],f[x-w[i]][y-1]+c[i]);
        FOR(x,0,m1) if(f[x][m2]>=v)
        {
            printf("%d\n",m1-x);
            bo=false;
            break;
        }
        if(bo) printf("-1\n");
    }
    return 0;
}

 

  6. 分組揹包

  分組就是一個組中只能選一件物品,剩下的就是普通的揹包問題了。

  hdu1712爲模板題。須要注意的是循環的順序,最一層是分組的循環,第二層爲容量的循環,第三層爲物品的循環。這裏咱們發現和前面有所不一樣,但證實一下能夠發現只有這樣才能夠保證每一個組中最多隻選擇一件物品。

#include <cstdio>
#include <cstring>
#define FOR(i,x,y) for(int i=x;i<=y;++i)
#define rFOR(i,x,y) for(int i=x;i>=y;--i)
#define MAX(a,b) a>b?a:b
int f[110],n,m,c[110];
int main()
{
    while(scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        memset(f,0,sizeof(f));
        FOR(k,1,n)
        {
            FOR(i,1,m) scanf("%d",&c[i]);
            rFOR(x,m,0) FOR(i,1,m) if(x>=i)
                f[x]=MAX(f[x],f[x-i]+c[i]);
        }
        printf("%d\n",f[m]);
    }
    return 0;
}

 

  7. 有依賴的揹包

相關文章
相關標籤/搜索