题目链接:点击打开链接
题目大意:略。
解题思路:多重背包。
AC 代码
#include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof a) using namespace std; typedef long long ll; const int MAXN = 10005; int dp[MAXN]; int val[MAXN],weight[MAXN],num[MAXN]; int n,m,init; void ZeroOne_Pack(int val,int weight,int m) { // for(int j=m;j>=weight;j--) // dp[j]=max(dp[j],dp[j-weight]+val); // 特殊情况,需要还原到原先最初 01背包 写法 j==[m,0] for(int j=m-1;j>=0;j--) // 这里 j 从 m-1 开始是因为 j 如果从 m 开始(也可以)但结果不变,没意义 { if(j+weight>m) dp[m]=min(dp[m],dp[j]+val); // 类似 j+weight-weight else dp[j+weight]=min(dp[j+weight],dp[j]+val); } } void Complete_Pack(int val,int weight,int m) { // for(int j=weight;j<=m;j++) // dp[j]=max(dp[j],dp[j-weight]+val); // 类似 01背包 一样修改,只需修改 完全背包 j 的特性即可 for(int j=0;j<=m-1;j++) { if(j+weight>m) dp[m]=min(dp[m],dp[j]+val); else dp[j+weight]=min(dp[j+weight],dp[j]+val); } } int Multi_Pack(int val[],int weight[],int n,int m) { for(int i=0;i<MAXN;i++) dp[i]=init; dp[0]=0; // 避免 max/min 函数使用时,一直是init数据 for(int i=0;i<n;i++) { if(num[i]*weight[i]>m) { Complete_Pack(val[i],weight[i],m); } else { int k=1; while(k<num[i]) { ZeroOne_Pack(k*val[i],k*weight[i],m); num[i]-=k; k<<=1; } ZeroOne_Pack(num[i]*val[i],num[i]*weight[i],m); } } return dp[m]; } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { for(int i=0;i<n;i++) scanf("%d",&weight[i]); for(int i=0;i<n;i++) scanf("%d",&val[i]); int msum=0; init=0; for(int i=0;i<n;i++) { scanf("%d",&num[i]); msum+=num[i]*weight[i]; init+=num[i]*val[i]; // 为初始化 dp[] 做准备,当然自定义一个数据也可,但可能发生意外,算出上界保险 } Multi_Pack(val,weight,n,m); printf(msum<m?"Impossible\n":"%d\n",dp[m]); } return 0; }