01揹包及其變式 UVA12536 HDU2126

概念:
01揹包是在M件物品取出若干件放在空間爲W的揹包裏,每件物品的體積爲W1,W2至Wn,與之相對應的價值爲P1,P2至Pn。01揹包是揹包問題中最簡單的問題。01揹包的約束條件是給定幾種物品,每種物品有且只有一個,並且有權值和體積兩個屬性。在01揹包問題中,因爲每種物品只有一個,對於每個物品只需要考慮選與不選兩種情況。如果不選擇將其放入揹包中,則不需要處理。如果選擇將其放入揹包中,由於不清楚之前放入的物品佔據了多大的空間,需要枚舉將這個物品放入揹包後可能佔據揹包空間的所有情況。
以上來源於百度百科

01揹包 是一個十分經典的模型,這個模型能夠套在許多問題上。對於這個問題的求解,dp可以說是最後的方法了。
其狀態轉移方程爲:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
當然可以將其優化爲一維數組,這些都是在01揹包dp上講的很清楚的東西了,如果不清楚那麼模擬一遍不妨是一種很好的辦法。

下面我們來看看一些題目:
1.裝箱問題
有一個箱子容量爲V(正整數,0≤V≤20000),同時有n個物品(0小於n≤30),每個物品有一個體積(正整數)。要求從n個物品中,任取若干個裝入箱內,使箱子的剩餘空間爲最小。
輸入v,n,在輸入n個物品。
輸出箱子的剩餘空間爲最小。

思路:
轉換爲01揹包 可以看做v = w
最後就是V-dp[V]

2 Jin Ge Jin Qu hao UVA12536
題目太長就不貼出來了
簡單來講就是要滿足最多歌曲的情況下使得時間最長
其實單獨看每一個問題都很好將其轉化爲01揹包 當放在一起的時候其實我們就用兩個dp來維護就行了 優先滿足數目 在其最優的情況下再去滿足時間最優

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
const int  maxn = 100005; 
int dp1[maxn] ,dp2[maxn] ,a[105];
int n,t;
int T;
int main(){
   scanf("%d",&T);
   for(int cas=1 ; cas<=T ;cas++){
      scanf("%d%d",&n,&t);   
      t--;  
     for(int i=0 ;i<n ;i++) 
       scanf("%d",&a[i]);

   memset(dp1 ,0 ,sizeof(dp1));
   memset(dp2 ,0 ,sizeof(dp2));

   for(int i=0 ;i<n ;i++) 
    for(int j=t; j>=a[i] ;j--){
      if(dp1[j] < dp1[j-a[i]]+1 ){
        dp1[j] = dp1[j-a[i]]+1;
        dp2[j] = dp2[j-a[i]]+a[i];
      }
      else if(dp1[j] == dp1[j-a[i]]+1)
       dp2[j] = max(dp2[j] , dp2[j-a[i]]+a[i]); 
    }
   printf("Case %d: %d %d\n",cas,dp1[t]+1,dp2[t]+678);
   }  
   return 0;
}

**3**Buy the souvenirs HDU2126
當寒假來臨時,很多人都會去旅行。一般來說,有很多紀念品出售,有時候旅行者會很高興地買一些。他們不僅可以把紀念品送給朋友和家人作爲禮物,還可以回味這些紀念品。總而言之,紀念品的價格並不十分昂貴,也令人喜愛和有趣。但是人們的預算是有限的,他們不能買太多。他們在欣賞了所有的紀念品之後,決定購買某些紀念品,購買時不能購買超過 1 件的同品種紀念品 (即對於同一個品種,要麼購買 1 件,要麼不購買)。

例如:

產品 A,價格 1 元
產品 B,價格 2 元
產品 C,價格 3 元
產品 D,價格 4 元
如果你只有 7 元錢,則最多可以選擇 3 種紀念品的組合,這 3 種紀念品的組合有 ABC (6), ABD (7) 共 2 種方案。如果你有 8 元錢的話,選擇最多種類的紀念品有 ABC (6), ABD (7), ACD (8) 共 3 種方案。如果你有 10 元人民幣,選擇品種最多的紀念品只有 ABCD (10) 這 1 種方案。

輸入
輸入首先包含一個整數 T,表示測試數據的組數。

對於每組測試數據,在第一行中有兩個整數 n, m,其中 n 是紀念品的數量,m 是擁有的預算資金。第二行包含 n 個整數,每個整數描述一種紀念品的價格。

所有的輸入數據和輸出結果都在 32 位整數的範圍內,0 ≤ m ≤ 500 ,0 < n ≤ 30, t ≤ 500,價格都是正整數。兩組測試數據之間有一個空行。

輸出
如果你可以買一些紀念品,則打印如下格式的結果:“You have S selection(s) to buy with K kind(s) of souvenirs.” (不含引號),其中 K 代表你可以買到的品種最多的紀念品, S 表示可以 K 種紀念品對應的的組合數量。

如果你帶的錢不夠 (無法購買任何紀念品),則打印結果“Sorry, you can’t buy anything.” (不含引號)。

思路:
與上題類似,只是這題需要維護的是能購買的種類和數量,所以有一個轉移的方程不一樣,但也很好理解。在此也就不多說了,注意初始條件。

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

#define MS(a,x) memset(a,x,sizeof(a))
#define maxn 505
int t;
int a[35];
int m,n;
int f[maxn][2]; //花掉k 0種類  1 selction 

void printw(){
  printf("Sorry, you can't buy anything.\n");
}
void printr(int s,int k){
  printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n",s,k); 
}

void init(){
  MS(a,0);
  MS(f,0);
  scanf("%d%d",&n,&m);// kinds  money 
  for(int i=1 ;i<=n; i++)
  scanf("%d",&a[i]);
}

void work(){
  for(int i=0 ;i<=m ;i++)
  f[i][1] = 1;

  for(int i=1; i<=n; i++)   
   {for(int k=m; k>=a[i] ;k--)
    { 
      if(f[k][0]==f[k-a[i]][0]+1)
      f[k][1] += f[k-a[i]][1];
      else if(f[k][0] < f[k-a[i]][0]+1)
        { 
           f[k][1] = f[k-a[i]][1]; 
           f[k][0] = f[k-a[i]][0]+1;
        }
    }
   }
   if(!f[m][0])printw();
   else printr(f[m][1],f[m][0]);     
}

int main(){
  scanf("%d",&t);
  while(t--){
   init();
   work();
  }
 return 0;
}

大致也就要說這麼多,感覺這幾個題目對01揹包有了一個很好的運用,同時也能加深對其的理解,以及提高了對有限制條件時的dp的寫法的思維。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章