算法提高课 第一章 动态规划② (背包)
一、01背包模型
423. 采药
二维朴素做法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int M = 1010,N = 105;
int t[N],w[M],n,m;
int f[N][M];
int main()
{
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d%d", &t[i], &w[i]);
}
for (int i = 1; i <= n; i ++ )
{
for(int j = 0;j<=m;j++)
{
f[i][j] = f[i-1][j];
if(t[i] <= j) f[i][j] = max(f[i][j],f[i-1][j-t[i]] + w[i]);
}
}
cout<<f[n][m]<<endl;
return 0;
}
优化到一维
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int M = 1010,N = 105;
int t[N],w[M],n,m;
int f[M];
int main()
{
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d%d", &t[i], &w[i]);
}
for (int i = 1; i <= n; i ++ )
{
for(int j = m;j>=t[i];j--)//注意:必须从后往前枚举
{
if(t[i] <= j) f[j] = max(f[j],f[j-t[i]] + w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
1024. 装箱问题
本题将体积视为价值,使用01背包模板即可
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 35,M = 20010;
int v[N],n,m;
int f[M];//f[i]:总体积不超过i的总体积的最大值
int main()
{
scanf("%d", &m);
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &v[i]);
}
for (int i = 1; i <= n; i ++ )
{
for (int j = m; j >= v[i]; j -- ) //一维除完全背包模型外必须从高到低枚举
{
f[j] = max(f[j],f[j-v[i]] + v[i]);
}
}
cout<<m - f[m]<<endl;
return 0;
}
1022. 宠物小精灵之收服
本题是01背包与多重花费代价的结合,用维数表示代价,用数组值表示获得的价值
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,M = 505,K = 105;
int n,m,k;
int vc[K],ve[K];
int f[N][M]; //f[i][j]:表示消耗精灵球数量不超过i,消耗体力值不超过j的所有方案中捕捉精灵个数的最大值
int main()
{
scanf("%d%d%d", &n, &m,&k);
for(int i = 1;i<=k;i++)
{
scanf("%d%d", &vc[i], &ve[i]);
}
for(int i = 1;i<=k;i++)
{
for(int j = n;j>=vc[i];j--) //减少一维,必须倒序枚举
{
for(int l = m-1;l>=ve[i];l--)//题目要求剩余体力值不能为0
{
f[j][l] = max(f[j][l],f[j-vc[i]][l-ve[i]] + 1);//01背包
}
}
}
cout<<f[n][m-1]<<' ';////题目要求剩余体力值不能为0
int r = m-1;//题目要求剩余体力值不能为0
while(r>0 && f[n][r-1] == f[n][m-1]) --r; //寻找捕捉数量最大且相同但消耗体力较小的方案
cout<<m - r<<endl;//输出剩余的最大体力值
return 0;
}
426. 开心的金明
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 30,M = 30010;
int v[N],w[N];
int f[M];
int n,m;
int main()
{
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i ++ )
{
int c,p;
scanf("%d%d", &c, &p);
v[i] = c,w[i] = c*p;
}
for(int i = 1;i<=n;i++)
{
for(int j = m;j>=v[i];j--)
{
f[j] = max(f[j],f[j-v[i]] + w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
734. 能量石
本题有魔法石会损失能量这一限制
因此如何枚举物品的次序是首要解决的问题,然后才可以套用01背包模型求解
我们这里采用贪心思路,从分析贪心解中两个相邻物品的次序来入手
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110,M = 1e4 + 10;
struct Node
{
int s;
int e;
int l;
bool operator<(struct Node& t) const //贪心确定最优次序
{
return s*t.l < t.s*l;
}
}stone[N];
int T,n,m;
int f[M];//f[i][j]:考虑前i个魔法石,吃完最后一个总用时恰好为j的所有方案中最大价值
int main()
{
scanf("%d", &T);
for(int k = 1;k<=T;k++)
{
memset(f,-0x3f3f3f3f,sizeof f);//由于是恰好,初始化为无穷小表示无法实现
f[0] = 0;//用时为0,一个也不吃的价值一定为0
scanf("%d", &n);
m = 0;
for (int i = 1; i <= n; i ++ )
{
int s,e,l;
scanf("%d%d%d", &s, &e,&l);
stone[i] = {s,e,l};
m += s;//体积就是吃掉所有魔法石所耗时间
}
sort(stone+1,stone+n+1);//贪心排序确定最优次序
for (int i = 1; i <= n; i ++ )//01背包模型求解
{
int v = stone[i].s,w = stone[i].e,l = stone[i].l;
for (int j = m; j >= v ; j -- )
{
f[j] = max(f[j],f[j-v] + w - (j - v)*l);//j时刻吃完,说明j-v时刻开始吃,价值会削减(j-v)*l
}
}
int ans = -1;
for (int i = 0; i <= m; i ++ ) //求最大价值
{
ans = max(ans,f[i]);
}
printf("Case #%d: %d\n",k,ans);
}
return 0;
}
二维费用的背包问题(不仅可以和01背包结合)
8. 二维费用的背包问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,V = 1050,M = 1050;
int v[N],g[N],w[N];
int f[V][M];//f[i][j]:总体积不超过i,总重量不超过j的所有方案中总价值最大的价值
int n,s,m;
int main()
{
scanf("%d%d%d", &n, &s,&m);
for(int i = 1;i<=n;i++)
{
scanf("%d%d%d", &v[i], &g[i],&w[i]);
}
for (int i = 1; i <= n; i ++ )
{
for (int j = s; j >= v[i]; j -- ) //降维优化写法:必须从后往前枚举
{
for(int k = m;k >= g[i];k--)//降维优化写法:必须从后往前枚举
{
f[j][k] = max(f[j][k],f[j-v[i]][k-g[i]] + w[i]);
}
}
}
cout<<f[s][m]<<endl;
}
1020. 潜水员
优化为两维的写法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 30,M = 80,K = 1010;
int f[N][M];//f[j][k]:前i个物品中选,耗氧量不小于j,耗氮量不小于k的方案中,总体积的最小值
int n,m,k;
int v1[K],v2[K],w[K];
int main()
{
scanf("%d%d%d", &n, &m,&k);
for (int i = 1; i <= k; i ++ )
{
cin>>v1[i]>>v2[i]>>w[i];
}
memset(f,0x3f3f3f3f,sizeof f);//要求最小值,其它不合法的状态总体积最小值为无穷大
f[0][0] = 0;//一个也不选,代价为0的,价值一定大于等于0
for (int i = 1; i <= k; i ++ )
{
for(int j = n;j>=0;j--)
{
for(int l = m;l>=0;l--)
{
f[j][l] = min(f[j][l],f[max(j-v1[i],0)][max(l-v2[i],0)] + w[i]);//max防止越界
}
}
}
cout<<f[n][m]<<endl;
return 0;
}
01背包之求方案数
278. 数字组合
二维朴素写法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105,M = 10010;
int n,m;
int f[N][M];//f[i][j]:所有只从前i个物品中选,总和恰好等于j的所有方案的个数
int a[N]; //把a看成体积
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
f[0][0] = 1; //一个数也不选,总和为0的方案恰好只有一个
for (int i = 1; i <= n; i ++ )
{
for (int j = 0; j <= m; j ++ )
{
f[i][j] += f[i-1][j];//不选a[i]的方案数
if(j>=a[i]) f[i][j] += f[i-1][j-a[i]]; //能选则加上可选的方案数
}
}
cout<<f[n][m]<<endl;
return 0;
}
优化为一维
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105,M = 10010;
int f[M];
int a[N],n,m;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
f[0] = 1;
for (int i = 1; i <= n; i ++ )
{
for(int j = m;j>=a[i];j--)
{
f[j] = f[j] + f[j-a[i]];
}
}
cout<<f[m]<<endl;
return 0;
}
1021. 货币系统
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20,M = 3010;
typedef long long LL;
LL f[N][M];//f[i][j]:前i个物品中,总体积恰好为j的所有方案的个数
int n,m;
int v[N];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &v[i]);
}
f[0][0] = 1; //一个都不选
for (int i = 1; i <= n; i ++ )
{
for(int j = 0;j<=m;j++)
{
f[i][j] += f[i-1][j];//不包含第i个物品
if(j>=v[i]) f[i][j] += f[i][j-v[i]];//包含第i个物品
}
}
cout<<f[n][m]<<endl;
return 0;
}
二、完全背包模型(求所有前缀的最大值)
当空间优化为1维后,只有完全背包问题的体积是从小到大循环的,其他从大到小循环
完全背包+求方案数
1023. 买书
f[i,j] = f[i-1,j] + f[i,j-v]
二维朴素写法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5,M = 1010;
int v[N] = {0,10,20,50,100};
int f[N][M]; //f[i][j]:前i个物品中,总花费恰好等于j的方案的个数
int m;
int main()
{
cin>>m;
f[0][0] = 1; //一个物品都不选,花费恰好为0,方案数为1
for (int i = 1; i <= 4; i ++ ) //二维朴素版本
{
for(int j = 0;j<=m;j++)
{
f[i][j] = f[i-1][j];
if(j >= v[i]) f[i][j] += f[i][j-v[i]]; //能装下才能选,推公式看博客
}
}
cout<<f[4][m]<<endl;
return 0;
}
优化为一维
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5,M = 1010;
int v[N] = {0,10,20,50,100};
int f[M]; //f[i][j]:前i个物品中,总花费恰好等于j的方案的个数
int m;
int main()
{
cin>>m;
f[0] = 1; //一个物品都不选,花费恰好为0,方案数为1
for (int i = 1; i <= 4; i ++ ) //优化为一维版本
{
for(int j = 0;j<=m;j++) //完全背包的优化无需从后往前枚举
{
if(j >= v[i]) f[j] += f[j-v[i]];
}
}
cout<<f[m]<<endl;
return 0;
}
532. 货币系统
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110,M = 25010;
bool f[M];//f[i][j]:前i个数中,当前体积为j的方案能否被满足
int T,n,m,a[N];
int main()
{
cin>>T;
while (T -- )
{
memset(f,0,sizeof f);
int ans = 0;
cin >> n;
for (int i = 1; i <= n; i ++ ) cin>>a[i];
sort(a+1,a+n+1);//某个金额只能被比它小的数表示出来,故需要升序排序
m = a[n];
f[0] = 1;//体积为0一定可以被表示出
for (int i = 1; i <= n; i ++ ) //完全背包问题,相当于求矩阵最大的秩
{
if(f[a[i]]) continue;//若a[i]可以被其它比它小的数表示,则跳过
++ans;//不能表示,则需要保留
for(int j = a[i];j<=m;j++)//完全背包模型,注意一维也是从小到大枚举
{
f[j] |= f[j-a[i]];
}
}
cout<<ans<<endl;
}
return 0;
}
三、多重背包模型(求滑动窗口内的最大值)
朴素方法
1019. 庆功会
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010,M = 6010;
int v[N],w[N],s,n,m,cnt;
int f[M];
int a,b,c;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ )
{
scanf("%d%d%d", &a, &b,&c);
for (int j = 0; j < c; j ++ )
{
int t = ++cnt;
v[t] = a,w[t] = b;
}
}
for (int i = 1; i <= cnt; i ++ )
{
for(int j = m;j>=v[i];j--)
{
f[j] = max(f[j],f[j-v[i]] + w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
单调队列优化
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20010;
int n,m;
int f[N],g[N],q[N];
int main()
{
cin>>n>>m;
for (int i = 0; i < n; i ++ )
{
int v,w,s;
cin>>v>>w>>s;
memcpy(g,f,sizeof f);
for (int j = 0; j < n; j ++ )
{
int hh = 0,tt = -1;
for (int k = j; k <= m; k += v )
{
if(hh <= tt && q[hh] < k - s*v) ++hh;
if(hh<=tt) f[k] = max(f[k],g[q[hh]] + (k - q[hh])/v*w);
while(hh<=tt && g[q[tt]] - (q[tt] - j)/v*w <= g[k] - (k - j)/v * w) tt--;
q[++tt] = k;
}
}
}
cout<<f[m]<<endl;
return 0;
}
四、分组背包问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110,M = 110,S = 110;
int v[S][N],w[S][N];//v/w[i][j]:第i组第j个物品的体积、价值
int f[N][M];//f[i][j]:前i组物品中,体积不大于j的所有方案的最大价值
int n,m;
int cnt[N];//cnt[i]:第i组物品数量
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
cin>>cnt[i];
for(int j = 1;j<=cnt[i];j++)
{
cin>>v[i][j]>>w[i][j];
}
}
for (int i = 1; i <= n; i ++ ) //枚举组数
{
for(int j = 0;j<=m;j++)
{
f[i][j] = f[i-1][j];//一个也不选
for(int k = 1;k<=cnt[i];k++)//枚举一组内的单个物品的选or不选
{
if(v[i][k]<=j) f[i][j] = max(f[i][j],f[i-1][j-v[i][k]] + w[i][k]);//体积够放才选
}
}
}
cout<<f[n][m]<<endl;
return 0;
}
1013. 机器分配
每家公司看做一个物品组,由于每家公司最终被分配的机器数量是固定的,因此对于分给第i个公司的不同机器数量可以看做是一个物品组内的物品
物品k的含义:分给第i个公司k台机器,物品体积为k,物品价值为w[i][k]
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15,M = 20;
int f[N][M];//f[i][j]:前i个物品组(公司)中,总数量不超过j的所有方案的最大价值
int w[N][M];//w[i][j]:把第j个机器分配给第i个公司可获得的利润
int n,m;
int way[N];//每个公司分配的机器个数
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
for(int j = 1;j <= m;j++)
{
scanf("%d", &w[i][j]);
}
}
for (int i = 1; i <= n; i ++ )
{
for(int j = 0;j<=m;j++)
{
f[i][j] = f[i-1][j]; //一个也不选
for(int k = 1;k<=j;k++) //枚举选择的个数,
{
f[i][j] = max(f[i][j],f[i-1][j-k] + w[i][k]);
}
}
}
cout<<f[n][m]<<endl;
int cur = m;//当前剩余机器个数
for(int i = n;i;i--)//求解最大价值是从前往后的,故推具体方案需要从后往前
{
if(cur==0) break;
for(int k = 0;k<=cur;k++)//枚举分配个数
{
if(f[i][cur] == f[i-1][cur-k] + w[i][k])//由公式推得:分给第i个机器k台设备
{
way[i] = k;
cur -= k;
break;
}
}
}
for (int i = 1; i <= n; i ++ )
{
cout<<i<<' '<<way[i]<<endl;
}
return 0;
}
487. 金明的预算方案
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
#define v first
#define w second
const int N = 70,M = 35000;
int n,m;
PII master[N];//表示所有主件
vector<PII>append[N];//表示所有主件的附件
int f[M];//优化一维写法:前i个主件与附件的方案中,总花费不超过j的所有方案的最大价值
int main()
{
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i ++ )
{
int v,w,q;
scanf("%d%d%d", &v, &w,&q);
if(!q) master[i] = {v,v*w};//题目要求求解价格*重要度的背包问题,first表示价格,second表示价格*重要度
else append[q].push_back({v,v*w});//附件的数据结构同主件
}
for(int i = 1;i<=n;i++)//枚举主件
{
if(master[i].v)//若该编号的主件存在,则进行求解
{
for(int j = m;j>=0;j--)//枚举体积(代价)
{
auto &apd = append[i];
for(int k = 0;k< 1<<apd.size();k++)//二进制优化枚举附件
{
int v = master[i].v,w = master[i].w;//表示主件+附件的体积与价值
for(int l = 0;l<apd.size();l++)//1表示选择该主件,0表示不选
{
if(k >> l & 1)//如果选择就加上体积与价值
{
v += apd[l].v,w += apd[l].w;
}
}
if(j>=v) f[j] = max(f[j],f[j-v] + w);//在体积范围内则求解
}
}
}
}
cout<<f[m]<<endl;
return 0;
}
四、背包问题求具体方案
12. 背包问题求具体方案
求解01背包时从后往前推
题目要求输出字典序最小的解,假设存在一个包含第1个物品的最优解,为了确保字典序最小那么我们必然要选第一个。那么问题就转化成从2~N这些物品中找到最优解。之前的f(i,j)记录的都是前i个物品总容量为j的最优解,那么我们现在将f(i,j)定义为从第i个元素到最后一个元素总容量为jj的最优解。接下来考虑状态转移:
f(i,j)=max(f(i+1,j),f(i+1,j−v[i])+w[i])
两种情况,第一种是不选第i个物品,那么最优解等同于从第i+1个物品到最后一个元素总容量为jj的最优解;第二种是选了第i个物品,那么最优解等于当前物品的价值w[i]加上从第i+1个物品到最后一个元素总容量为j−v[i]的最优解。
计算完状态表示后,考虑如何的到最小字典序的解。首先f(1,m)肯定是最大价值,那么我们便开始考虑能否选取第1个物品呢。
如果f(1,m)=f(2,m−v[1])+w[1],说明选取了第1个物品可以得到最优解。
如果f(1,m)=f(2,m),说明不选取第一个物品才能得到最优解。
如果f(1,m)=f(2,m)=f(2,m−v[1])+w[1],说明选不选都可以得到最优解,但是为了考虑字典序最小,我们也需要选取该物品。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,M = 1010;
int n,m;
int v[N],w[N];
int f[N][M];//f[i][j]:i~n个物品中,总体积不超过j的所有方案中,价值最大的
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
scanf("%d%d", &v[i], &w[i]);
}
for(int i = n;i>=1;i--) //必须从后往前求解01背包
{
for(int j = 0;j<=m;j++)
{
f[i][j] = f[i+1][j];
if(j >= v[i]) f[i][j] = max(f[i][j],f[i+1][j-v[i]] + w[i]);
}
}
int cur = m; //表示当前背包剩余容量
for (int i = 1; i <= n; i ++ ) //从前往后推,确保字典序最小
{
if(i==n && cur >= v[i]) //特判,防止f数组发生越界
{
cout<<i;
break;
}
if(cur <= 0) break; //体积不够了直接退出
if(cur >= v[i] && f[i][cur] == f[i+1][cur-v[i]] + w[i]) //根据状态转移方程,说明i是被选中的
{
cout<<i<<' ';
cur -= v[i];//体积一定要更新
}
}
return 0;
}
五、多重背包问题
7. 混合背包问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,M = 1010;
int n,m;
int f[M];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
int v,w,s;
scanf("%d%d%d", &v, &w,&s);
if(s==0) //完全背包问题
{
for(int j = v;j<=m;j++) //必须从小到大枚举
{
f[j] = max(f[j],f[j-v] + w);
}
}
else
{
if(s==-1) s = 1;//01背包看做特殊的多重背包问题
for(int k = 1;k<=s;k<<=1) //二进制优化多重背包
{
for(int j = m;j>=k*v;j--)
{
f[j] = max(f[j],f[j-k*v] + k*w);
}
s -= k;
}
if(s) //处理剩余个数的物品
{
for(int j = m;j>=s*v;j--)
{
f[j] = max(f[j],f[j-s*v] + s*w);
}
}
}
}
cout<<f[m]<<endl;
return 0;
}
六、有依赖的背包问题
10. 有依赖的背包问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105,M = 105;
int v[N],w[N],n,m;
int h[N],e[M],ne[M],idx;
int p,root;
int f[N][M];
void add(int a,int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void dfs(int u)
{
for(int i = h[u];i!=-1;i=ne[i]) //分组背包思想,把每个子树看成一个组,循环物品组
{
int son = e[i];
dfs(son);
//分组背包
for(int j = m - v[u];j>=0;j--)//循环体积
{
for(int k = 0;k<=j;k++)//循环决策,遍历子节点的组合
{
f[u][j] = max(f[u][j],f[u][j-k] + f[son][k]);//用k个体积来装子树里的物品,选价值最大的
}
}
}
//将物品u加进去
for(int i = m;i>=v[u];i--) f[u][i] = f[u][i-v[u]] + w[u];
for(int i = 0;i<v[u];i++) f[u][i] = 0;//体积不够的话,就不能选
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
scanf("%d%d%d", &v[i], &w[i],&p);
if(p!=-1)
{
add(p,i);//边从父节点指向子节点
}
else root = i;
}
dfs(root);
cout<<f[root][m]<<endl;
return 0;
}
七、背包问题求解最优方案数
11. 背包问题求方案数
用最短路求最短路条数的思想求解最优解方案数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,M = 1010,mod = 1e9 + 7;
int f[M];//f[j]表示体积不大于j的所有方案的最大价值
int g[M];//g[j]表示体积恰好是j的最优方案的个数
int v[N],w[N];
int n,m;
int ans;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
scanf("%d%d", &v[i], &w[i]);
}
g[0] = 1;//体积为0时一个也不选价值为0,仅一种方案,必是最优的
for (int i = 1; i <= n; i ++ )
{
for (int j = m; j >= v[i]; j -- )
{
int tmp = max(f[j],f[j-v[i]] + w[i]);//先找出较大价值,切记不要赋值给f
int cnt = 0;//记录最优方案的个数
if(f[j] == tmp) cnt = (cnt + g[j]) % mod;//不选时价值大
if(f[j-v[i]] + w[i] == tmp) cnt = (cnt + g[j-v[i]]) % mod;//选时价值大
//最后再更新赋值
f[j] = tmp;
g[j] = cnt;
}
}
for (int i = 0; i <= m; i ++ )//由于g表示体积恰好,故存在装不满时价值也为最大的情况,因此需要遍历
{
if(f[i] == f[m]) ans += g[i];
}
cout<<ans<<endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)