2021.6.8背包总结
T1
解析:二维01背包板子,不多赘述
#include<bits/stdc++.h>
using namespace std;
const int N = 405;
int w1[N],w2[N],val[N],dp[N][N];
int m1,m2,n;
int main()
{
scanf("%d%d",&m1,&m2);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&val[i],&w1[i],&w2[i]);
for(int i=1;i<=n;i++)
for(int j=m1;j>=w1[i];j--)
for(int k=m2;k>=w2[i];k--)
dp[j][k]=max(dp[j][k],dp[j-w1[i]][k-w2[i]]+val[i]);
printf("%d\n",dp[m1][m2]);
return 0;
}
/*
6 5
4
10 2 2
20 3 2
40 4 3
30 3 3
*/
T2
解析:输出答案就是简单的有限背包,关键在于如何输出路径。过程中num[i][j]表示第i个物品在总体积达到j时选了多少个
最后递归输出路径,但是要先找到达到最大价值所需的最少费用(now),否则输出就会错乱
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 505;
int n,m,p[N],w[105];
ll eff[105][105],sum;
ll ans[N][N];
ll dp[N][N];
ll num[N][N];
ll pre[N];
void print(int i,int now)
{
if(!i) return;
print(i-1,now-num[i][now]*w[i]);
printf("%d\n",num[i][now]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&w[i],&p[i]);
for(int j=1;j<=p[i];j++)
{
scanf("%lld",&eff[i][j]);
}
}
for(int i=1;i<=n;i++)
for(int j=m;j>=w[i];j--)
for(int k=1;k<=p[i];k++)
if(j>=k*w[i])
{
if(dp[j][0]<dp[j-k*w[i]][0]+eff[i][k])
{
dp[j][0]=dp[j-k*w[i]][0]+eff[i][k];
num[i][j]=k;
}
}
int now=m;
printf("%lld\n",dp[m][0]);
while(dp[now][0]==dp[now-1][0]) now--;
print(n,now);
return 0;
}
/*
3 10
1 3 1 2 2
2 3 2 4 6
3 3 2 1 10
*/
(或者也可以用dp记录每个物品怎么选)
代码如下
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 505;
int n,m,p[N],w[105];
ll eff[105][105],sum;
ll ans[N][N];
ll dp[N][N];
ll pre[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&w[i],&p[i]);
for(int j=1;j<=p[i];j++)
{
scanf("%lld",&eff[i][j]);
}
}
for(int i=1;i<=n;i++)
for(int j=m;j>=w[i];j--)
for(int k=1;k<=p[i];k++)
if(j>=k*w[i])
{
if(dp[j][0]<dp[j-k*w[i]][0]+eff[i][k])
{
dp[j][0]=dp[j-k*w[i]][0]+eff[i][k];
//printf("花费%d时,第%d种魔法升级为%d\n",j,i,k);
//
dp[j][i]=k;
for(int a=i-1;a>=1;a--)
dp[j][a]=dp[j-k*w[i]][a];
}
if(dp[j-k*w[i]][0]+eff[i][k]==dp[j][0]){
int sum1=0,sum2=0;
for(int a=1;a<=n;a++){
sum1+=w[a]*dp[j][a];
}
sum2+=k*w[i];
for(int a=i-1;a>=1;a--){
sum2+=w[a]*dp[j-k*w[i]][a];
}
if(sum2<sum1){
dp[j][i]=k;
for(int a=i-1;a>=1;a--){
dp[j][a]=dp[j-k*w[i]][a];
}
}
}
}
for(int i=0;i<=n;i++)
printf("%d\n",dp[m][i]);
return 0;
}
/*
3 10
1 3 1 2 2
2 3 2 4 6
3 3 2 1 10
*/
T3
解析:把每个有果子的树取出来,就变成了费用为2*(i+j)的多重背包。
注意事项:
- dp数组开到N*N
- 体力最后不能为0
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
#define ll long long
int mp[N][N],n,m,a,b,m1,m2;
ll dp[N];
int p[N*N],w[N*N],cnt,val[N*N];
int main()
{
scanf("%d%d%d%d",&a,&b,&m1,&m2);
m2--;
m=min(m1,m2);
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
{
scanf("%d",&mp[i][j]);
w[++cnt]=2*(i+j),val[cnt]=mp[i][j];
}
cnt=0;
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
{
int tmp;
scanf("%d",&tmp);
p[++cnt]=tmp;
}
for(int i=1;i<=cnt;i++)
for(int j=m;j>=w[i];j--)
for(int k=1;k<=p[i];k++)
if(j>=k*w[i])
dp[j]=max(dp[j],dp[j-k*w[i]]+k*val[i]);
printf("%lld",dp[m]);
return 0;
}
/*
4 4 13 20
10 0 0 0
0 0 10 0
0 0 10 0
0 0 0 0
1 0 0 0
0 0 2 0
0 0 4 0
0 0 0 0
*/
T4
解析:经典题
分类讨论:
- 只选择当前主件
- 选择当前主件和第1个附件
- 选择当前主件和第2个附件
- 选择当前主件和第1、2个附件
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 3.2e4+5 , M = 1e2+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
ll ret = 0 ;char ch = ' ' , c = getchar();
while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
return ch == '-' ? -ret : ret;
}
int n,m;
int w[M],v[M],ma[M][3],cnt,cntm,wa[M],va[M];
int dp[N],to[N];
signed main(){
n = read() , m = read();
int a,b,c;
for(int i = 1 ; i <= m ; i ++){
a = read() , b = read() , c = read();
if(!c)
to[i] = ++cnt,w[cnt] = a, v[cnt] = b * a;
else
ma[to[c]][ ++ma[to[c]][0] ] = ++cntm , wa[cntm] = a , va[cntm] = b * a;
}
for(int i = 1 ; i <= cnt ; i ++){
for(int j = n ; j >= w[i] ; j --){
dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
if(j >= w[i] + wa[ma[i][1]])
dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][1]]] + v[i] + va[ma[i][1]]);
if(j >= w[i] + wa[ma[i][2]])
dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][2]]] + v[i] + va[ma[i][2]]);
if(j >= w[i] + wa[ma[i][1]] + wa[ma[i][2]])
dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][1]]-wa[ma[i][2]]] + v[i] + va[ma[i][1]] + va[ma[i][2]]);
}
}
int ans = -INF;
for(int i = 0 ; i <= n ; i ++)
ans = max(ans,dp[i]);
// printf("%d:%d\n",i,dp[i]);
printf("%d",ans);
return 0;
}