揹包問題合集

AcWing 423. 採藥

題幹

辰辰是個天資聰穎的孩子,他的夢想是成爲世界上最偉大的醫師。
爲此,他想拜附近最有威望的醫師爲師。
醫師爲了判斷他的資質,給他出了一個難題。
醫師把他帶到一個到處都是草藥的山洞裏對他說:“孩子,這個山洞裏有一些不同的草藥,採每一株都需要一些時間,每一株也有它自身的價值。我會給你一段時間,在這段時間裏,你可以採到一些草藥。如果你是一個聰明的孩子,你應該可以讓採到的草藥的總價值最大。”
如果你是辰辰,你能完成這個任務嗎?
輸入格式
輸入文件的第一行有兩個整數T和M,用一個空格隔開,T代表總共能夠用來採藥的時間,M代表山洞裏的草藥的數目。
接下來的M行每行包括兩個在1到100之間(包括1和100)的整數,分別表示採摘某株草藥的時間和這株草藥的價值。

輸出格式
輸出文件包括一行,這一行只包含一個整數,表示在規定的時間內,可以採到的草藥的最大總價值。

數據範圍
1≤T≤1000
1≤M≤100
輸入樣例:
70 3
71 100
69 1
1 2
輸出樣例:
3

思路

總結下題意,就是揹包問題,時間代表揹包的大小,求最大價值,用一維揹包就可以表示了。
f[i]表示經過了i時間所能採到的最大價值。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

int f[1100];
int main(){
    int time,m,t,w;
    scanf("%d%d",&time,&m);
    for(int i=0;i<m;i++){
        scanf("%d%d",&t,&w);
        for(int j=time;j>=t;j--){
        //從最大時間開始遍歷,如果想採當前藥至少需要t時間
            f[j]=max(f[j-t]+w,f[j]);
        }
    }
    printf("%d\n",f[time]);
    return 0;
}

AcWing 1024. 裝箱問題

題幹

有一個箱子容量爲 V,同時有 n 個物品,每個物品有一個體積(正整數)。
要求 n 個物品中,任取若干個裝入箱內,使箱子的剩餘空間爲最小。
輸入格式
第一行是一個整數 V,表示箱子容量。
第二行是一個整數 n,表示物品數。
接下來 n 行,每行一個正整數(不超過10000),分別表示這 n 個物品的各自體積。
輸出格式
一個整數,表示箱子剩餘空間。

數據範圍
0<V≤20000
0<n≤30
輸入樣例:
24
6
8
3
12
7
9
7
輸出樣例:
0

思路

總結下題意,還是標準的揹包問題,剩餘空間最小也就是求箱子的容量和最大。
f[i]表示容量爲i的箱子所使用的最大容量和

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
int f[30000];
int main(){
    int v,n,x;
    scanf("%d%d",&v,&n);
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        for(int j=v;j>=x;j--){
        //依舊是從最大值開始,遍歷到臨界
            f[j]=max(f[j],f[j-x]+x);
        }
    }
    printf("%d\n",v-f[v]);
    return 0;
}

AcWing 1022. 寵物小精靈之收服

題幹

寵物小精靈是一部講述小智和他的搭檔皮卡丘一起冒險的故事。
一天,小智和皮卡丘來到了小精靈狩獵場,裏面有很多珍貴的野生寵物小精靈。
小智也想收服其中的一些小精靈。
然而,野生的小精靈並不那麼容易被收服。
對於每一個野生小精靈而言,小智可能需要使用很多個精靈球才能收服它,而在收服過程中,野生小精靈也會對皮卡丘造成一定的傷害(從而減少皮卡丘的體力)。
當皮卡丘的體力小於等於0時,小智就必須結束狩獵(因爲他需要給皮卡丘療傷),而使得皮卡丘體力小於等於0的野生小精靈也不會被小智收服。
當小智的精靈球用完時,狩獵也宣告結束。
我們假設小智遇到野生小精靈時有兩個選擇:收服它,或者離開它。
如果小智選擇了收服,那麼一定會扔出能夠收服該小精靈的精靈球,而皮卡丘也一定會受到相應的傷害;如果選擇離開它,那麼小智不會損失精靈球,皮卡丘也不會損失體力。
小智的目標有兩個:主要目標是收服儘可能多的野生小精靈;如果可以收服的小精靈數量一樣,小智希望皮卡丘受到的傷害越小(剩餘體力越大),因爲他們還要繼續冒險。
現在已知小智的精靈球數量和皮卡丘的初始體力,已知每一個小精靈需要的用於收服的精靈球數目和它在被收服過程中會對皮卡丘造成的傷害數目。
請問,小智該如何選擇收服哪些小精靈以達到他的目標呢?
輸入格式
輸入數據的第一行包含三個整數:N,M,K,分別代表小智的精靈球數量、皮卡丘初始的體力值、野生小精靈的數量。
之後的K行,每一行代表一個野生小精靈,包括兩個整數:收服該小精靈需要的精靈球的數量,以及收服過程中對皮卡丘造成的傷害。
輸出格式
輸出爲一行,包含兩個整數:C,R,分別表示最多收服C個小精靈,以及收服C個小精靈時皮卡丘的剩餘體力值最多爲R。

數據範圍
0<N≤1000,
0<M≤500,
0<K≤100
輸入樣例1:
10 100 5
7 10
2 40
2 50
1 20
4 20
輸出樣例1:
3 30
輸入樣例2:
10 100 5
8 110
12 10
20 10
5 200
1 110
輸出樣例2:
0 100

思路

總結下很長的題意,就是你有n個球,m點生命值,k個寶可夢,抓當前寶可夢需要消耗xi個球、yi點生命值,求最多抓取數,相同抓取數時最大的剩餘生命值。
①因爲有兩種限制,所以需要用二維揹包,f[i][j]表示當前有i個球,使用的生命值爲j(即攻擊力)最多可以抓幾個寶可夢。這樣f[n][m]就可以表示最大抓取數。
然後考慮最大剩餘生命值,因爲j表示的是使用過的生命值(即怪物的攻擊力)所以我們要使j儘可能的小,這樣就是找f[n][j]==f[n][m]的最小j。

//第一種
//yxc大佬的做法
//f[i][j]表示消耗了i個球,j點攻擊力情況下的最大捕捉數。
//然後考慮最少生命消耗,遍歷f[n][j],尋找與f[n][m]相同的捕捉數, j從m-1(因爲生命值不能爲0)遞減(因爲要求最大剩餘生命值)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int x[1100],y[510];
int f[1100][510];
int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<k;i++){
        scanf("%d%d",&x[i],&y[i]);
        for(int j=n;j>=x[i];j--){
            for(int l=m-1;l>=y[i];l--){
                f[j][l]=max(f[j][l],f[j-x[i]][l-y[i]]+1);
            }
        }
    }
    printf("%d",f[n][m-1]);
    k=m-1;
    while(k>0&&f[n][m-1]==f[n][k-1]){
        k--;
    }
    printf(" %d\n",m-k);
    return 0;
}

②依舊是考慮二維揹包問題,但是考慮到數據範圍nϵ[0,1000],kϵ[0,100]n\epsilon[0,1000],k\epsilon[0,100]所以將能抓取的寶可夢數和消耗生命值作爲兩個維度;f[i][j]表示已經使用的精靈球數。
這樣先將f初始化爲正無窮INF,f[0][j]=0(因爲沒抓到寶可夢不消耗球),
f[i][j]=min(f[i][j],f[i1][jr]+c)f[i][j]=min(f[i][j],f[i-1][j-r]+c)(需保證]f[i1][jr]+c<=nf[i-1][j-r]+c<=n)
這樣從k開始找到第一個f[ans][m-1]!=INF的ans就是最大捕捉數。
然後考慮最大生命值即最小生命消耗,同樣是找f[ans][i]<=n的最小i。

//第二種
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int f[110][510];
int main(){
    int n,m,k,c,r;
    memset(f,0x3f,sizeof(f));
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<m;i++)
        f[0][i]=0;
    for(int i=0;i<k;i++){
        scanf("%d%d",&c,&r);
        for(int j=k;j;j--){
            for(int l=m-1;l>=r;l--){
                if(f[j-1][l-r]+c<=n){
                    f[j][l]=min(f[j][l],f[j-1][l-r]+c);
                }
            }
        }
    }
    int ans=0;
    for(int i=k;i;i--){
        if(f[i][m-1]!=0x3f3f3f3f){
            ans=i;
            break;
        }
    }
    printf("%d ",ans);
    k=m-1;
    while(k&&f[ans][k-1]<=n)  k--;
    printf("%d\n",m-k);
    return 0;
}

AcWing 278. 數字組合

題幹:

給定N個正整數A1,A2,…,AN,從中選出若干個數,使它們的和爲M,求有多少種選擇方案。
輸入格式
第一行包含兩個整數N和M。
第二行包含N個整數,表示A1,A2,…,AN
輸出格式
包含一個整數,表示可選方案數。

數據範圍
1≤N≤100,
1≤M≤10000,
1≤Ai≤1000
輸入樣例:
4 4
1 1 2 2
輸出樣例:
3

思路:

從n個數中選擇任意個的和爲m的方案數。因爲只能選擇一次,所以看作是01揹包問題,既然要求方案數那就不是+1而是+f[i-v]了,初始值爲f[0]=1。f[i]表示何爲i的方案數;依舊是倒退。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int f[11000];
int main(){
    int n,m,v;
    scanf("%d%d",&n,&m);
    f[0]=1;
    for(int i=0;i<n;i++){
        scanf("%d",&v);
        for(int j=m;j>=v;j--){
            f[j]+=f[j-v];
        }
    }
    printf("%d\n",f[m]);
    return 0;
}

AcWing 532. 貨幣系統

題幹:

在網友的國度中共有 n 種不同面額的貨幣,第 i 種貨幣的面額爲 a[i],你可以假設每一種貨幣都有無窮多張。
爲了方便,我們把貨幣種數爲 n、面額數組爲 a[1…n] 的貨幣系統記作 (n,a)。 
在一個完善的貨幣系統中,每一個非負整數的金額 x 都應該可以被表示出,即對每一個非負整數 x,都存在 n 個非負整數 t[i] 滿足 a[i]× t[i] 的和爲 x。
然而,在網友的國度中,貨幣系統可能是不完善的,即可能存在金額 x 不能被該貨幣系統表示出。
例如在貨幣系統 n=3, a=[2,5,9] 中,金額 1,3 就無法被表示出來。 
兩個貨幣系統 (n,a) 和 (m,b) 是等價的,當且僅當對於任意非負整數 x,它要麼均可以被兩個貨幣系統表出,要麼不能被其中任何一個表出。 
現在網友們打算簡化一下貨幣系統。
他們希望找到一個貨幣系統 (m,b),滿足 (m,b) 與原來的貨幣系統 (n,a) 等價,且 m 儘可能的小。
他們希望你來協助完成這個艱鉅的任務:找到最小的 m。
輸入格式
輸入文件的第一行包含一個整數 T,表示數據的組數。
接下來按照如下格式分別給出T組數據。 
每組數據的第一行包含一個正整數 n。
接下來一行包含 n 個由空格隔開的正整數 a[i]。
輸出格式
輸出文件共有T行,對於每組數據,輸出一行一個正整數,表示所有與 (n,a) 等價的貨幣系統 (m,b) 中,最小的 m。

數據範圍
1≤n≤100,
1≤a[i]≤25000,
1≤T≤20
輸入樣例:
2
4
3 19 10 6
5
11 29 13 19 17
輸出樣例:
2
5

思路:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int x[110];
int f[26000];
int main(){
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&x[i]);
        }
        sort(x+1,x+n+1);
        memset(f,0,sizeof(f));
        f[0]=1;
        int mx=x[n];
        for(int i=1;i<=n;i++){
            for(int j=x[i];j<=mx;j++){
                f[j]+=f[j-x[i]];
            }
        }
        int ans=n;
        for(int i=1;i<=n;i++){
            //printf("%d\n",f[x[i]]);
            if(f[x[i]]>1)  ans--;
        }
        printf("%d\n",ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章