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
#include
#include
#include
#include
#include
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 ;iscanf("%d",&a[i]);

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

   for(int i=0 ;ifor(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
#include
#include
#include
#include
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的写法的思维。

你可能感兴趣的:(dp)