【01背包专题】--入门思考

来源:互联网 发布:国家统计局工业数据库 编辑:程序博客网 时间:2024/05/17 01:11

【问题】:


给出n个物品的大小,问容量为V的背包,最多能装入多大的物品?

【思路】

可以想到一种最最暴力的方法,每个物品可以选择放入背包或者不放入,那么n个物品就有2^n种放入背包的情况。

这种方法效率太低,不过可以从中得到点感悟。

1.对结果进行设计:定义输出用dp[V]表示,意思是对n个物品进行选择后,容量为V的背包最多能容纳dp[V]值大小的物品。

2.对过程进行分析:每个物品可以选择放入背包或者不放入,那么选择到第n个时:

a.第n个如果不放入背包,也就是说第n个物品不会占用到背包的容量,那么取到第n个时的dp[V]与取到第n-1个时的dp[V]是一样的;

b.第n个如果放入背包,占用了一部份背包容量,能提供给装前n-1个物品的背包容量只有V-a[n],那么取到第n个时的dp[V]就等于取到第n-1个时的dp[V-a[n]]加上当前选择放入的物品大小a[n]。


【如何实现】

过程分析中的前提是,当选择进行到第n个时,前n-1个物品的dp[0...V]都已经算出,选择进行到第1个物品时,dp[0...V]都为0,当然了,因为之前没物品。

<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-size:18px;">memset(dp,0,sizeof(dp));for(i=0;i<N;i++){   for(j=V;j-a[i]>=0;j--)//必须V到0遍历,否则同一个物品的选择会重复算造成错误。   {dp[j]=max(dp[j],dp[j-a[i]]+a[i]);   }}</span></span>

【经典例题】

HDU 2546

思路:

还有5块的时候,可以任意买,当然是用来买最贵的了。所以先把剩余的钱拿出5块留到最后买最贵的,问题就转化成了如何花掉更多的剩余的钱。与上述如何使一个固定大小的背包装最大的物品本质相同。

代码:

<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-size:18px;">#include<stdio.h>#include<stdlib.h>#include<string>int n;int value[1001];int v;int dp[1001];//输出为v-dp[v]+5-value[n-1]int cmp(const void*a,const void*b){return *(int *)a-*(int *)b;}int max(int a,int b){return a>b?a:b;}int main(){while(~scanf("%d",&n)&&n){int i,j;for(i=0;i<n;i++){scanf("%d",&value[i]);}scanf("%d",&v);qsort(value,n,sizeof(value[0]),cmp);v=v-5;if(v<0) {printf("%d\n",v+5);continue;}memset(dp,0,sizeof(dp));for(i=0;i<n-1;i++){for(j=v;j-value[i]>=0;j--){dp[j]=max(dp[j],dp[j-value[i]]+value[i]);}}printf("%d\n",v-dp[v]+5-value[n-1]);}return 0;}</span></span>

HDU 1171

思路:

要均分价值,没法均分的话,尽量接近。问题可以理解成,从n个物品中,挑选出的物品价值和要尽量接近总价值一半。上述背包问题也可以理解为,装入的物品大小要尽量接近背包大小。本质一致。

<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-size:18px;">#include<stdio.h>#include<string>int N;int dp[300000];//输出为,max(dp[V],sum-dp[V]),min(dp[V],sum-dp[V])int V;int value[5001];int max(int a,int b){return a>b?a:b;}int min(int a,int b){return a<b?a:b;}int main(){while(~scanf("%d",&N)&&N>=0){int sum=0,count=0;int i,j;for(i=0;i<N;i++){int a,b;scanf("%d%d",&a,&b);sum+=a*b;for(j=0;j<b;j++){value[count++]=a;}}V=(sum+1)/2;memset(dp,0,sizeof(dp));for(i=0;i<count;i++){for(j=V;j-value[i]>=0;j--){dp[j]=max(dp[j],dp[j-value[i]]+value[i]);}}printf("%d %d\n",max(dp[V],sum-dp[V]),min(dp[V],sum-dp[V]));}return 0;}</span></span>

HDU 2602

这题给出了物品的价值,要使得固定大小的背包装入物品的价值最大。很裸的背包问题。

代码:

<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-size:18px;">#include<stdio.h>#include<string>int T,N,V;int value[1001],cost[1001];int dp[1001];//输出为dp[V]int max(int a,int b){return a>b?a:b;}int main(){scanf("%d",&T);while(T--){scanf("%d%d",&N,&V);int i,j;for(i=0;i<N;i++){scanf("%d",&value[i]);}for(i=0;i<N;i++){scanf("%d",&cost[i]);}memset(dp,0,sizeof(dp));for(i=0;i<N;i++){for(j=V;j-cost[i]>=0;j--){dp[j]=max(dp[j],dp[j-cost[i]]+value[i]);}}printf("%d\n",dp[V]);}return 0;}</span></span>

HDU 2639

求第k个最优解,可以将数组多开一维,每次更新时用两个数组,一个存将物品放入背包的第k最优解,一个存不放入背包的第k最优解,再合并。

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include<stdio.h>#include<string>int N,V,K;int value[101];int cost[101];int dp[1001][31];//输出dp[V][k]int A[32],B[32];int main(){int T;scanf("%d",&T);while(T--){scanf("%d%d%d",&N,&V,&K);int i,j,k;for(i=0;i<N;i++) scanf("%d",&value[i]);for(i=0;i<N;i++) scanf("%d",&cost[i]);memset(dp,0,sizeof(dp));for(i=0;i<N;i++){for(j=V;j-cost[i]>=0;j--){for(k=1;k<=K;k++){A[k]=dp[j-cost[i]][k]+value[i];//A存放入的第k大B[k]=dp[j][k];//B存不放入的第k大}//并入dp[j][k]int a1=1,b1=1;A[K+1]=B[K+1]=0;for(k=1;k<=K&&(A[a1]||B[b1]);){if(A[a1]>B[b1]) {if(A[a1]!=dp[j][k-1]){dp[j][k]=A[a1];k++;}a1++;}else{if(B[b1]!=dp[j][k-1]){dp[j][k]=B[b1];k++;}b1++;}}}}printf("%d\n",dp[V][K]);}return 0;}</span>

0 0