基础背包(一)
hdu 2602 01背包
许多年前,在泰迪的家乡,有一个人被称为“骨收藏家”。这个人喜欢收集各种各样的骨头,比如狗的,牛的,他也去了坟墓
采骨者有一个体积为v的大袋子,在他的采集过程中有很多骨头,很明显,不同的骨头有不同的价值和体积,现在考虑到每一块骨头的价值,你能计算出采骨者能得到的总价值的最大值吗
输入
第一行包含整数t,即事例数。
接下来是t例,每例三行,第一行包含两个整数n,v,(n<=1000,v<=1000),表示骨骼数量和他的包的体积。第二行包含n个整数,表示每个骨骼的值。第三行包含n个整数,表示每个骨骼的体积。
产量
每行一个整数,表示总值的最大值(此数字将小于2 31)。
解题思路:简单01背包
一维解法:
#include<iostream> #include<cstring> using namespace std; typedef long long ll; const int maxn=1100; int n,V,dp[maxn],weight[maxn],value[maxn],num[maxn]; int main(){ int t; cin>>t; while(t--){ cin>>n>>V; for(int i=1;i<=n;i++)cin>>value[i]; for(int i=1;i<=n;i++)cin>>weight[i]; memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++){ for(int j=V;j>=weight[i];j--) dp[j]=max(dp[j],dp[j-weight[i]]+value[i]); } cout<<dp[V]<<endl; } return 0; }
二维解法:
#include<iostream> #include<cstring> using namespace std; typedef long long ll; const int maxn=1100; int n,V,dp[maxn][maxn],weight[maxn],value[maxn],num[maxn]; int main(){ int t; cin>>t; while(t--){ cin>>n>>V; for(int i=1;i<=n;i++)cin>>value[i]; for(int i=1;i<=n;i++)cin>>weight[i]; memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++){ for(int j=0;j<=V;j++){ if(j>=weight[i])dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]); else dp[i][j]=dp[i-1][j]; } } cout<<dp[n][V]<<endl; } return 0; }
hdu 1114 完全背包
题目大意:
输入空的存钱罐的重量以及满了的重量
输入硬币种类数t
以下t行输入t种硬币每个硬币的重量以及面值
求此情况下该存钱罐至少能存的钱数,无解就输出无解
解题思路:完全背包问题,注意初始化,初始化为无穷大表示不合法,如果计算最后还是无穷大则表示无解,把max改成min就好了
一维解法:
#include<iostream> #include<cstring> using namespace std; typedef long long ll; const int maxn=11000; int n,V,dp[maxn],weight[maxn],value[maxn],num[maxn]; int main(){ int t; cin>>t; while(t--){ int v1,v2; cin>>v1>>v2; V=v2-v1; cin>>n; for(int i=1;i<=n;i++)cin>>value[i]>>weight[i]; memset(dp,0x3f,sizeof(dp)); dp[0]=0; for(int i=1;i<=n;i++) for(int j=weight[i];j<=V;j++) dp[j]=min(dp[j],dp[j-weight[i]]+value[i]); if(dp[V]==0x3f3f3f3f)puts("This is impossible."); else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[V]); } return 0; }
二维解法:
#include<iostream> #include<cstring> using namespace std; typedef long long ll; const int maxn=11000; int n,V,dp[505][maxn],weight[maxn],value[maxn],num[maxn]; int main(){ int t; cin>>t; while(t--){ int v1,v2; cin>>v1>>v2; V=v2-v1; cin>>n; for(int i=1;i<=n;i++)cin>>value[i]>>weight[i]; memset(dp,0x3f,sizeof(dp)); for(int i=0;i<=n;i++)dp[i][0]=0; for(int i=1;i<=n;i++){ for(int j=1;j<=V;j++){ if(j>=weight[i]) dp[i][j]=min(dp[i-1][j],dp[i][j-weight[i]]+value[i]); else dp[i][j]=dp[i-1][j]; } } if(dp[n][V]==0x3f3f3f3f)puts("This is impossible."); else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[n][V]); } return 0; }
hdu 1171 多重背包
题目大意:给你n个物品的价值和物品的数量,如何分使得A,B所得价值最接近并且A的价值不能小于B
解题思路:完全背包,背包的容量为价值的总和的一半
#include<iostream> #include<cstring> #include<cmath> using namespace std; typedef long long ll; const int maxn=110000; int n,V,dp[55][maxn],weight[maxn],value[maxn],num[maxn]; int main(){ while(cin>>n){ if(n<0)break; int sum=0; memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++){ cin>>value[i]>>num[i]; weight[i]=value[i]; sum+=value[i]*num[i]; } V=sum/2; if(sum%2)V++; for(int i=1;i<=n;i++) for(int j=0;j<=num[i];j++) for(int k=V;k>=j*weight[i];k--) dp[i][k]=max(dp[i-1][k],dp[i-1][k-j*weight[i]]+j*value[i]); cout<<max(dp[n][V],sum-dp[n][V])<<" "<<min(dp[n][V],sum-dp[n][V])<<endl; } return 0; }
hdu 2844 多重背包+二进制优化
题目大意:给出n种硬币的价值以及数目,求这n种硬币可以在1-m中可以支付的价格有多少种
解题思路:硬币的价值同时也是硬币的代价,dp[j]表示容量为j的背包所可以得到的最大价值,如果等于j表示可以支付价钱j
#include<iostream> #include<cstring> #include<cmath> using namespace std; typedef long long ll; const int maxn=110000; int n,m,V,dp[maxn],value[maxn],num[maxn],vis[maxn],weight[maxn]; int main(){ while(cin>>n>>m){ if(n==0&&m==0)break; for(int i=1;i<=n;i++) cin>>value[i]; for(int i=1;i<=n;i++) cin>>num[i]; int ans=0; memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++){ if(value[i]*num[i]>m){ for(int j=value[i];j<=m;j++) //完全背包 dp[j]=max(dp[j],dp[j-value[i]]+value[i]); }else{ //二进制优化01背包 for(int j=1;j<=num[i];j*=2){ for(int k=m;k>=j*value[i];k--) dp[k]=max(dp[k],dp[k-j*value[i]]+j*value[i]); num[i]-=j; } if(num[i]){ for(int k=m;k>=num[i]*value[i];k--) dp[k]=max(dp[k],dp[k-num[i]*value[i]]+num[i]*value[i]); } } } for(int i=1;i<=m;i++) if(dp[i]==i)ans++; cout<<ans<<endl; } return 0; }
hdu 1059 多重背包+二进制优化
题目大意:给你6种不同价值的硬币的数量,价值分别是1~6,问你能否可以将这些硬币分成两堆,每堆的价值一样。
解题思路:和hdu1171很像,把价值总和的一半看成背包容量,计算形成最大的价值是否可以达到价值总和的一半
#include<iostream> #include<cstring> #include<cmath> using namespace std; typedef long long ll; const int maxn=110000; int n,V,dp[maxn],value[maxn],num[maxn],vis[maxn],weight[maxn]; int main(){ int T=0; while(cin>>num[1]>>num[2]>>num[3]>>num[4]>>num[5]>>num[6]){ int sum=0; n=6; T++; for(int i=1;i<=6;i++)sum+=num[i]; if(sum==0)break; sum=0; for(int i=1;i<=6;i++){ value[i]=i; sum+=value[i]*num[i]; } printf("Collection #%d:\n",T); if(sum%2){ puts("Can't be divided."); puts(""); continue; } V=sum/2; memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++){ if(value[i]*num[i]>V){ for(int j=value[i];j<=V;j++) //完全背包 dp[j]=max(dp[j],dp[j-value[i]]+value[i]); }else{ //二进制优化 for(int j=1;j<=num[i];j*=2){ for(int k=V;k>=j*value[i];k--) dp[k]=max(dp[k],dp[k-j*value[i]]+j*value[i]); num[i]-=j; } if(num[i]){ for(int k=V;k>=num[i]*value[i];k--) dp[k]=max(dp[k],dp[k-num[i]*value[i]]+num[i]*value[i]); } } } if(dp[V]==V) puts("Can be divided."); else puts("Can't be divided."); puts(""); } return 0; }