背包问题详解

前提是你 提交于 2019-12-19 18:45:33

Nyoj289苹果(01背包)

时间限制:3000 ms  |  内存限制:65535 KB

难度:3

描述

ctest有n个苹果,要将它放入容量为v的背包。给出第i个苹果的大小和价钱,求出能放入背包的苹果的总价钱最大值。

输入

有多组测试数据,每组测试数据第一行为2个正整数,分别代表苹果的个数n和背包的容量v,n、v同时为0时结束测试,此时不输出。接下来的n行,每行2个正整数,用空格隔开,分别代表苹果的大小c和价钱w。所有输入数字的范围大于等于0,小于等于1000。

输出

对每组测试数据输出一个整数,代表能放入背包的苹果的总价值。

样例输入

3 3

1 1

2 1

3 1

0 0

样例输出

2

#include<stdio.h>

#include<string.h>

#define N 1001

int dp[N];

int max(int a,int b)

{

         if(a>b)

         return a;

         else

         return b;

}

int main()

{

         int n,v,i,j,c,w;

         while(scanf("%d %d",&n,&v))

         {

                   if(n==0&&v==0)

                   break;

                   memset(dp,0,sizeof(dp));

                   for(i=1;i<=n;i++)

                   {

                            scanf("%d %d",&c,&w);

                            for(j=v;j>=c;j--)

                            dp[j]=max(dp[j],dp[j-c]+w);

                   }

                   printf("%d\n",dp[v]);

         }

         return 0;

}

 

又见01背包

时间限制:1000 ms  |  内存限制:65535 KB

难度:3

描述

    有n个重量和价值分别为wi 和 vi 的 物品,从这些物品中选择总重量不超过 W 

的物品,求所有挑选方案中物品价值总和的最大值。

  1 <= n <=100

  1 <= wi <= 10^7

  1 <= vi <= 100

  1 <= W <= 10^9

输入

多组测试数据。
每组测试数据第一行输入,n 和 W ,接下来有n行,每行输入两个数,代表第i个物品的wi 和 vi。

输出

满足题意的最大价值,每组测试数据占一行。

样例输入

4 5
2 3
1 2
3 4
2 2

样例输出

7

这道题其实和一般的01背包没有什么区别,只是这道题目按照正常的思维去做不行了,因为容量太大,开个10^9的数组开不了,所以这时候就需要换种思维,这个题刚开始没理解,后来在网上在题解,发现都说是互换重量和价值,但是一直没理解什么意思,后来,仔细想了想那个最最基础的01背包是怎么推出来的才想通了, 也不能说是互换价值和重量,那样其实并不能加深理解,做完这个题之后,发现又对背包理解深了一点,写个博客,留下纪念

这道题很容易发现其实重量很大,达到10^9,但是价值很小啊,现在就来推一下这个所谓的“互换”是怎么来的 (其实我觉得还不如从最原始的来,不叫做“互换”好理解点), 最原始的那个式子

dp[i][j]表示当取 i 个, 重量为 j 的时候背包的最大价值,状态转移方程就是 dp[i][j] = max(dp[i - 1][j], dp[i-1][j - weight[i]] + value[i]), 这个式子的意思想必大家都明白吧,前面的那个意思是不取当前这个,后面的这个是取上当前这个物品, 后来再经空间优化之后变成了dp[j] = max(dp[j], dp[j - weight[i]] + value[i]), 仔细观察会发现二维数组时,那两种状态都是i - 1,所以就可以去掉,但是得注意,循环遍历的时候要逆序,正序的话就成完全背包了, 忘了说这个dp[j]表示什么了, dp[j]就是 当取到重量为j 的时候的最大价值。弄明白了这些。这时候就可以来看这个题了, 题目要求和普通的01背包一样,求能装的最大价值,普通方法就是直接找最大价值,现在要换种思维,找最小的重量, 因为同样价值,重量越小,那么最后能装的价值就可能越大,所以这个dp[i][j]就表示 当 取 i 个, 价值为j 的时候的最小重量,状态转移方程为 dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - value[i]] + weight[i]), 和那个最初推的一样,不再罗嗦,空间优化之后状态转移方程为dp[j] = min(dp[j], dp[j - value[i]] + weight[i]), 同样的意思,dp[j]表示 价值为j 的时候的最小重量,到最后只要从最大价值往下遍历这个dp数组,只要找到dp[j] <= 背包重量的时候就直接输出 j , 这时候j就是最大的。

#include<stdio.h>

#include<string.h>

#define Min(a,b) a<b?a:b

#define N 10003

int dp[N];

int weight[102];

int value[102];

int main()

{

         int n,w;

         while(~scanf("%d %d",&n,&w))

         {

                   int sum=0;

                   for(int i=0;i<n;i++)

                   {

                            scanf("%d%d",&weight[i],&value[i]);

                            sum+=value[i];

                   }

                   memset(dp,111,sizeof(dp));

                   dp[0]=0;

                   for(int i=0;i<n;i++)

                   {

                            for(int j=sum;j>=value[i];j--)

                                     dp[j]=Min(dp[j],dp[j-value[i]]+weight[i]);

                   }

                   for(int i=sum;i>=0;i--)

                   if(dp[i]<=w)

                   {

                            printf("%d\n",i);

                            break;

                   }

         }

         return 0;

}

Nyoj311完全背包

时间限制:3000 ms  |  内存限制:65535 KB

难度:4

描述

直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。本题要求是背包恰好装满背包时,求出最大价值总和是多少。如果不能恰好装满背包,输出NO

输入

第一行: N 表示有多少组测试数据(N<7)。 
接下来每组测试数据的第一行有两个整数M,V。 M表示物品种类的数目,V表示背包的总容量。(0<M<=2000,0<V<=50000)
接下来的M行每行有两个整数c,w分别表示每种物品的重量和价值(0<c<100000,0<w<100000)

输出

对应每组测试数据输出结果(如果能恰好装满背包,输出装满背包时背包内物品的最大价值总和。 如果不能恰好装满背包,输出NO)

样例输入

2

1 5

2 2

2 5

2 2

5 1

样例输出

NO

1

#include<stdio.h>

#include<string.h>

int d[50001];

int main()

{

         int n,i,j;

         int c,v,a,b;

         scanf("%d",&n);

         while(n--)

         {

                   scanf("%d%d",&c,&v);

                   memset(d,-11,sizeof(d));

                   d[0]=0;

                   for(i=0;i<c;i++)

                   {

                            scanf("%d%d",&a,&b);

                            for(j=a;j<=v;j++)

                            {

                                     if(d[j]<d[j-a]+b)

                                     d[j]=d[j-a]+b;

                            }       

                   }

                   if(d[v]<0)

                            printf("NO\n");

                   else

                            printf("%d\n",d[v]);

         }

         return 0;

}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!