背包模拟
概况
主要对有限背包、完全背包、背包记录方案、有依赖背包进行了考查,重点在于考察学生对于背包模型的灵活运用以及学生对背包板子的熟练度
A.PACK
思路:
二维背包板子,还好数据小
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
x=x*10+c-'0';c=getchar();}
return x*f;
}
ll dp[1005][1005],t[1005],v[1005],g[1005];
int main(){
ll V=read(),G=read(),n=read();
for(int i=1;i<=n;i++){
t[i]=read();v[i]=read();g[i]=read();}
for(int i=1;i<=n;i++)
for(int j=V;j>=v[i];j--)
for(int k=G;k>=g[i];k--)
dp[j][k]=max(dp[j][k],dp[j-v[i]][k-g[i]]+t[i]);
printf("%lld",dp[V][G]);
return 0;
}
B.DIABLO
思路:
求解最优值部分为有限背包板子,对于记录方案,这是个我一直以来都没太学会的东西,最后在f神的帮助下完成了,大概思路如下:对于第i件物品,我们记录它在剩余为j被选择时的件数k,然后从总费用开始递归输出,要注意的是这里总费用不一定是给出的,而有可能更小,这时就需要提前处理
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
x=x*10+c-'0';c=getchar();}
return x*f;
}
ll dp[1005],c[1005],p[1005],w[1005][1005],pre[1005][10005];
void print(int now_n,int now_dp){
if(now_n==0)return;
print(now_n-1,now_dp-pre[now_n][now_dp]*c[now_n]);
printf("%lld\n",pre[now_n][now_dp]);
}
int main(){
ll n=read(),m=read();
for(int i=1;i<=n;i++){
c[i]=read();p[i]=read();for(int j=1;j<=p[i];j++)w[i][j]=read();}
for(int i=1;i<=n;i++)
for(int j=m;j>=c[i];j--)
for(int k=1;k<=p[i]&&k*c[i]<=j;k++){
if(dp[j-k*c[i]]+w[i][k]>dp[j]){
dp[j]=dp[j-k*c[i]]+w[i][k];
pre[i][j]=k;
}
}
while(dp[m]==dp[m-1]) m--;
printf("%lld\n",dp[m]);
print(n,m);
return 0;
}
收获:
1.背包方案记录
2.递归输出方案
C.MANOR
思路:
显然对于这道题,体力和时间的限制是一定的,每个格子的体力和时间是一定的并且是一致的,于是我们只需要对每个格子的消耗进行预处理,进行有限背包即可
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
x=x*10+c-'0';c=getchar();}
return x*f;
}
ll dp[1005],c[1005][1005],v[1005][1005],t[1005][1005];
int main(){
ll n=read(),m=read(),ti=read(),a=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
v[i][j]=read(),c[i][j]=(i+j)*2;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
t[i][j]=read();
for(int x=1;x<=n;x++)
for(int y=1;y<=m;y++)
for(int j=min(ti,a-1);j>=c[x][y];j--)
for(int k=1;k<=t[x][y]&&k*c[x][y]<=j;k++)
dp[j]=max(dp[j],dp[j-k*c[x][y]]+k*v[x][y]);
printf("%lld",dp[min(ti,a-1)]);
return 0;
}
D.BUDGET
思路:
对于每个附件,我们将其存到主件下面,因为最多有两个附件,所以DP部分直接特判即可。但是需要特别注意输入时编号不是按照主件输入的顺序而是按照所有物件输入的顺序
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
x=x*10+c-'0';c=getchar();}
return x*f;
}
struct goods{
ll p,im,num;}a[50005][5];
ll dp[50005];
int main(){
ll m=read(),N=read(),n=0;
for(int i=1;i<=N;i++){
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
if(!z){
a[i][0].p=x;a[i][0].im=y;}
else if(!a[z][1].p){
a[z][1].p=x;a[z][1].im=y;}
else{
a[z][2].p=x;a[z][2].im=y;}
}
for(int i=1;i<=N;i++){
for(int j=m;j>=a[i][0].p;j--){
ll p0=a[i][0].p,p1=a[i][1].p,p2=a[i][2].p;
ll im0=a[i][0].im,im1=a[i][1].im,im2=a[i][2].im;
ll num=a[i][0].num;
dp[j]=max(dp[j],dp[j-p0]+p0*im0);
if(p0+p1<=j&&im1)dp[j]=max(dp[j],dp[j-p0-p1]+p0*im0+p1*im1);
if(p0+p2<=j&&im2)dp[j]=max(dp[j],dp[j-p0-p2]+p0*im0+p2*im2);
if(p0+p1+p2<=j&&im1&&im2)dp[j]=max(dp[j],dp[j-p0-p1-p2]+p0*im0+p1*im1+p2*im2);
}
}
printf("%lld",dp[m]);
return 0;
}