【Acwing提高】DP·背包
推荐:炒鸡棒的适合萌新的DP题单(大概?)
【Acwing提高】DP·背包
文章目录
- 【Acwing提高】DP·背包
- 知识点
- 题目
- 采药
- 装箱问题
- 宠物小精灵之收服
- 数字组合
- 买书
- 货币系统1021
- 货币系统531
- 多重背包问题 III
- 庆功会
- 混合背包问题
- 二维费用的背包问题
- 潜水员
- 机器分配
- 开心的金明
- 有依赖的背包问题
- 背包问题求方案数
- 背包问题求具体方案
- 能量石
- 金明的预算方案
知识点
题目 | 扩展方式 | 扩展来源 |
---|---|---|
采药 | 裸的 | 01 |
装箱问题 | 价值=体积,最小转求最大 | 01 |
宠物小精灵之收服 | 价值为1,费用不为0,多关键字 | 01二维费用 |
数字组合 | 费用恰好,求方案数 | 01 |
买书 | 费用恰好,求方案数 | 完全背包 |
货币系统1021 | 求方案数,开longlong | 完全背包 |
货币系统531 | 求方案数,模型转化,可行性 | 完全背包 |
多重背包问题 III | 单调队列优化(滑动窗口) | 多重 |
庆功会 | 裸的 | 多重背包 |
混合背包问题 | 大杂烩 | 01,多重,完全 |
二维费用的背包问题 | 二维费用 | 01 |
潜水员 | 费用变为至少,求min | 二维费用01 |
机器分配 | 抽象转化,求具体方案 | 分组背包 |
开心的金明 | 裸的 | 01 |
有依赖的背包问题 | 树形依赖 | 树形dp,分组,金明的预算方案 |
背包问题求方案数 | 最优解方案数(最短路条数),体积恰好 | 01 |
背包问题求具体方案 | 物品逆向,字典序最小(贪心,求具体方案) | 01 |
能量石 | 贪心 | 01 |
金明的预算方案 | 有依赖背包,两层依赖 | 分组 |
题目
采药
思路
裸的01没啥好讲的,但是这里代码写的比自己的好,因为边输入边计算,节省空间了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,v,w;
const int N=1005;
ll f[N];int main()
{cin>>m>>n;for(int i=1;i<=n;i++){cin>>v>>w;//输入同时计算for(int j=m;j>=v;j--)f[j]=max(f[j],f[j-v]+w);}cout<<f[m];return 0;
}
装箱问题
思路
价值=体积,最小转求最大
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,v;
const int N=2e4+10;
ll f[N];int main()
{cin>>m>>n;for(int i=1;i<=n;i++){cin>>v;for(int j=m;j>=v;j--)f[j]=max(f[j],f[j-v]+v);}cout<<m-f[m];return 0;
}
宠物小精灵之收服
思路
捕获精灵数越多越好,如果相同,剩余体力越多越好
捕获精灵数越多越好
二维费用背包(具体推导看后面题目),价值为1,体力值不能为0是需要注意的点
同时,对于背包问题,体积和价值是可以互换的,因此根据数据范围选择体积和价值可以有效地降低时间复杂度
另外一种题解:(体力、精灵数为费用,精灵球数为价值) O(K2M)O(K^2M)O(K2M)
剩余体力越多越好找到最小的k使得f[V1][k]==f[V1][V2−1]f[V_1][k]==f[V_1][V_2-1]f[V1][k]==f[V1][V2−1]
代码
(体力、精灵球数为费用、精灵数为价值)O(NMK)O(NMK)O(NMK)
#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=510;
int n,V1,V2;
int f[N][M];
int main()
{cin>>V1>>V2>>n;for(int i=1;i<=n;i++){int v1,v2;cin>>v1>>v2;for(int j=V1;j>=v1;j--)for(int k=V2-1;k>=v2;k--)//体力值不能为0所以不能从V2开始f[j][k]=max(f[j][k],f[j-v1][k-v2]+1);}cout<<f[V1][V2-1]<<" ";int k=V2-1;while(k>0&&f[V1][k-1]==f[V1][V2-1])k--;//先判断后操作cout<<V2-k<<endl;return 0;
}
数字组合
思路
求方案数,并且恰好
f[i][j]=f[i−1][j]+f[i−1][j−vi]f[i][j]=f[i-1][j]+f[i-1][j-v_i]f[i][j]=f[i−1][j]+f[i−1][j−vi]不要写成
f[i][j]=f[i−1][j]+f[i−1][j−vi]+1f[i][j]=f[i-1][j]+f[i-1][j-v_i]+1f[i][j]=f[i−1][j]+f[i−1][j−vi]+1
初始化的时候记得f[0][0]=1f[0][0]=1f[0][0]=1其余为0
注意区分体积最多为j的初始化
代码
#include<bits/stdc++.h>
using namespace std;const int N=10010;int n,m;
int f[N];
int main()
{cin>>n>>m;f[0]=1;//初始化为1,其他为0for(int i=0;i<n;i++){int v;cin>>v;for(int j=m;j>=v;j--)f[j]+=f[j-v];}cout<<f[m];return 0;
}
买书
思路
费用恰好(要花完),完全背包求方案数,和数字组合很小
代码
#include<bits/stdc++.h>
using namespace std;const int N=1e3+10;
int f[N];
int a[N]={10,20,50,100};
int n;
int main()
{cin>>n;f[0]=1;//注意初始化for(int i=0;i<4;i++){for(int j=a[i];j<=n;j++)f[j]+=f[j-a[i]];}cout<<f[n];
}
货币系统1021
思路
记得开long long
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e3+10;
typedef long long ll;
int n,m,k;
ll v,f[N];
int main()
{cin>>n>>m;f[0]=1;for(int i=1;i<=n;i++){cin>>v;for(int j=v;j<=m;j++)f[j]+=f[j-v];}cout<<f[m];return 0;
}
货币系统531
思路
根据题意,若两套货币系统相等,能表示的集合要相同,不能表示的集合也要相同。
可以得出:最优解一定从原序列中选出来,问题可以转化为某个面值是否必选。(最优性–>可行性问题–>可行性–>方案数)
那么,如何判断某一面值是否必选,可以转化为排序后,前1~i-1个面值凑成a[i]的方案数,若方案数为0,则a[i]必选,否则a[i]可以被前面的替换掉不必选。
模型转化为完全背包求方案数。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=110,M=25010;
int n;
int f[M],a[N];
int main()
{int T;cin>>T;while(T--){cin>>n;for(int i=0;i<n;i++)cin>>a[i];sort(a,a+n);int m=a[n-1];memset(f,0,sizeof f);f[0]=1;int res=0;for(int i=0;i<n;i++){if(!f[a[i]])res++;for(int j=a[i];j<=m;j++)f[j]+=f[j-a[i]];}cout<<res<<endl;}return 0;
}
多重背包问题 III
思路
比较好的题解
看数据模拟
滑动窗口图解
如何处理w的差值
摘自上面的博客里
所以,我们可以得到
dp[j] = dp[j]
dp[j+v] = max(dp[j] + w, dp[j+v])
dp[j+2v] = max(dp[j] + 2w, dp[j+v] + w, dp[j+2v])
dp[j+3v] = max(dp[j] + 3w, dp[j+v] + 2w, dp[j+2v] + w, dp[j+3v])
...
但是,这个队列中前面的数,每次都会增加一个 w ,所以我们需要做一些转换
dp[j] = dp[j]
dp[j+v] = max(dp[j], dp[j+v] - w) + w
dp[j+2v] = max(dp[j], dp[j+v] - w, dp[j+2v] - 2w) + 2w
dp[j+3v] = max(dp[j], dp[j+v] - w, dp[j+2v] - 2w, dp[j+3v] - 3w) + 3w
...
这样,每次入队的值是 dp[j+k*v] - k*w
代码转化
放到下面代码里就是dp[k]-(k-j)/v*w
滑动窗口模板(以滑动窗口中min为例子)
hh = 0; tt = -1;// 初始化for (int i = 0; i < n; ++ i)//遍历数轴{if (i - k + 1 > q[hh]) ++ hh;//如果第i项加进去超过窗口宽度,队首出队while (hh <= tt && a[i] <= a[q[tt]]) -- tt;//保持a[i]>a[q[tt]],单调递增q[++ tt] = i;if (i + 1 >= k) printf("%d ", a[q[hh]]);//队头即最小值}
代码
#include<bits/stdc++.h>
using namespace std;const int N=2e4+10;
int n,m;
int f[N],g[N],q[N];
//f存储的是第i层,g存储第i-1层,q存储的是f,g数组中的下标(体积,例如:q[5]=r+3v);
//g[k]=f[i-1][k]
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<v;j++)//枚举余数{int hh=0,tt=-1;for(int k=j;k<=m;k+=v)//枚举体积(即数轴坐标){if(hh<=tt&&q[hh]<k-s*v)hh++;//看有没有超过s件(窗口长度太长)if(hh<=tt)f[k]=max(f[k],g[q[hh]]+(k-q[hh])/v*w);//每次窗口max就是队头坐标转化后g[q[hh]]+(k-q[hh])/v*wwhile(hh<=tt&&g[q[tt]]-(q[tt]-j)/v*w<=g[k]-(k-j)/v*w)tt--;//(k-q[hh])/v和(k-j)/v就是下标q[++tt]=k;}}}cout<<f[m]<<endl;return 0;
}
庆功会
思路
裸的多重背包
代码
#include<bits/stdc++.h>
using namespace std;
const int N=6010;
int n,m;
int f[N];
int main()
{cin>>n>>m;for(int i=0;i<n;i++){int v,w,s;cin>>v>>w>>s;for(int j=m;j>=0;j--)for(int k=0;k<=s&&k*v<=j;k++)//注意这个f[j]=max(f[j],f[j-k*v]+k*w);}cout<<f[m];return 0;
}
混合背包问题
思路
只要看第i个物品时啥类型背包就行了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+10;
int f[N];
int n,m;
int main()
{cin>>n>>m;for(int i=0;i<n;i++){int v,w,s;cin>>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*=2)//多重背包二进制{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;
}
二维费用的背包问题
思路
代码
#include<bits/stdc++.h>
using namespace std;
int n,V1,V2,v1,v2,w;
const int N=1e3+10;
int f[N][N];
int main()
{cin>>n>>V1>>V2;for(int i=1;i<=n;i++){cin>>v1>>v2>>w;for(int j=V1;j>=v1;j--)for(int k=V2;k>=v2;k--)f[j][k]=max(f[j][k],f[j-v1][k-v2]+w);}cout<<f[V1][V2];return 0;
}
潜水员
思路
推荐:背包初始化
代码
#include<bits/stdc++.h>
using namespace std;const int N=22,M=80;int n,m,k;
int f[N][M];int main()
{cin>>n>>m>>k;memset(f,0x3f,sizeof f);//初始化为INF,只有合法地转移,f[0][0]=0;while(k--){int v1,v2,w;cin>>v1>>v2>>w;for(int j=n;j>=0;j--)//>=0,负数是合法的,因为至少for(int k=m;k>=0;k--)f[j][k]=min(f[j][k],f[max(0,j-v1)][max(0,k-v2)]+w);/*for(int j=n;j>=v1;j--)//这样不能让负数也转移for(int k=m;k>=v2;k--)f[j][k]=min(f[j][k],f[j-v1][k-v2]+w);//max(0,j-v1)不是说到0就可以了,只是因为负数至少等效于取0,仍旧要转移的*/}cout<<f[n][m]<<endl;return 0;
}
机器分配
思路
把公司当作物品组,机器数当作体积,价值为所给矩阵,转化为分组背包问题(同一个物品组只能选一个物品或者不选)
代码
#include<bits/stdc++.h>
using namespace std;int w[20][20],f[20][20],way[20];
int n,m;int main()
{cin>>n>>m;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>w[i][j];for(int i=1;i<=n;i++)for(int j=0;j<=m;j++){f[i][j]=f[i-1][j];//第i组一个都不选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 j=m;for(int i=n;i;i--)for(int k=0;k<=j;k++)if(f[i][j]==f[i-1][j-k]+w[i][k]){way[i]=k;j-=k;break;//找到直接跳出就行了}for(int i=1;i<=n;i++)cout<<i<<" "<<way[i]<<endl;return 0;
}
开心的金明
思路
裸的01
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e4+10;
int n,m,k;
int v,w,dp[N];
int main()
{cin>>m>>n;for(int i=1;i<=n;i++){cin>>v>>w;w*=v;for(int j=m;j>=v;j--)dp[j]=max(dp[j],dp[j-v]+w);}cout<<dp[m];return 0;
}
有依赖的背包问题
思路
树形DP,把每个子树作为物品组,以体积来划分
啊这里对于树中的每个节点来说,就是一个分组背包问题。每个子节点是一组物品,
每个子节点的不同体积和每个体积所对应的最大价值,就是这个物品组中的物品。
图解版的题解
只是把分组背包的组换成根节点,物品换成子树。区别是第三重循环决策不再按选哪个物品(时间复杂度太高)而是分配个每个子树的体积
代码
#include<bits/stdc++.h>
using namespace std;const int N=110;
int n,m;
int h[N],e[N],ne[N],idx;
int v[N],w[N];
int f[N][N];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;i=ne[i])//循环物品组{int son=e[i];dfs(e[i]);//分组背包//这个时候当前结点我们看成是分组背包中的一个组,子节点的每一种选择我们都看作是组内一种物品for(int j=m-v[u];j>=0;j--)//循环体积,注意m-v[u]默认选根节点for(int k=0;k<=j;k++)//循环决策,给子节点son分配多少体积f[u][j]=max(f[u][j],f[u][j-k]+f[son][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()
{cin>>n>>m;memset(h,-1,sizeof h);//初始化int root;for(int i=1;i<=n;i++){int p;cin>>v[i]>>w[i]>>p;if(p==-1)root=i;//根else add(p,i);}dfs(root);cout<<f[root][m]<<endl;return 0;
}
背包问题求方案数
思路
可以想成求最短路条数
法一:
定义f[i][j]f[i][j]f[i][j]为从前i个物品中选,体积恰好为j的选法集合
f[i][j]=max(f[i−1]j],f[i−1][j−v]+w)f[i][j]=max(f[i-1]j],f[i-1][j-v]+w)f[i][j]=max(f[i−1]j],f[i−1][j−v]+w)
开一个g[i][j]g[i][j]g[i][j]存f[i][j]f[i][j]f[i][j]取到最优解方案数
不选第i个大g[i][j]=g[i−1][j]g[i][j]=g[i-1][j]g[i][j]=g[i−1][j]
选第i个大g[i][j]=g[i−1][j−v]g[i][j]=g[i-1][j-v]g[i][j]=g[i−1][j−v]
选不选第i个一样大g[i][j]=g[i−1][j]+g[i−1][j−v]g[i][j]=g[i-1][j]+g[i-1][j-v]g[i][j]=g[i−1][j]+g[i−1][j−v]
因为体积是恰好,所以要遍历一遍,求最大值(f[m]不是最大值)
然后再遍历一遍看看有没有相等的再求和
注意初始化
法二:
滑稽大佬的题解
关注两种方法的初始化问题
代码
法一
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int N=1e3+10,mod=1e9+7;
ll f[N],g[N];
int main()
{cin>>n>>m;memset(f,-0x3f,sizeof f);//不能使体积恰好为j的不能被递推f[0]=0,g[0]=1;//显然选体积为0价值为0,而什么都不选的选法为1for(int i=0;i<n;i++){int v,w;cin>>v>>w;for(int j=m;j>=v;j--){if(f[j]<f[j-v]+w){f[j]=f[j-v]+w;g[j]=g[j-v]%mod;}else if(f[j]==f[j-v]+w){g[j]=(g[j]+g[j-v])%mod;}}}ll res=0,cnt=0;for(int i=0;i<=m;i++)res=max(res,f[i]);for(int i=0;i<=m;i++)if(res==f[i])cnt=(cnt+g[i])%mod;cout<<cnt;return 0;
}
法二代码(滑稽大佬的)
#include<iostream>
#include<algorithm>using namespace std;const int N=1010,mod=1e9+7;int f[N],g[N];int main()
{int n,m;cin>>n>>m;for(int i=0;i<=m;i++) g[i]=1;//初始化时我们易知,不论是哪个体积下,总有一个对应的最大价值,方案数为1for(int i=1;i<=n;i++){int v,w;cin>>v>>w;for(int j=m;j>=v;j--){if(f[j]<f[j-v]+w){g[j]=g[j-v]; //当f[j]<f[j-v]+w时,说明g[j]只能从上层转移过来了f[j]=f[j-v]+w;}else if(f[j]==f[j-v]+w) g[j]=(g[j]+g[j-v])%mod;//若相等,说明存在了2个节点,他们路径都符合条件//可以递推到g[j]}}cout<< g[m] <<endl;//最后输出这个体积不超过m对应最大价值的方案数即可!return 0;
}
背包问题求具体方案
思路
推荐题解参考
求获得最大价值的具体方案,并令字典序最小
求具体方案:判断出每个每个物品是否被选
首先不能进行状态压缩
记录方案,从哪个路径走到f[n][m]f[n][m]f[n][m]
怎么判断?
如果f[n][m]=f[n−1][m]那么从不选第n个转移过来如果f[n][m]=f[n-1][m]那么从不选第n个转移过来 如果f[n][m]=f[n−1][m]那么从不选第n个转移过来如果f[n][m]=f[n-1][m-v[n]]+w[n]$那么从选第n个转移过来
也可能两个都可以,即第n个物品可选可不选
字典序最小:贪心
从第一个开始,每个物品有选有三种情况
只能选–>一定选
只能不选–>一定不选
可选可不选–>一定选
但是我们一般dp的时候是倒着推具体方案的,我们要最小字典序是要从前往后推如何解决呢?那么在输入之后,dp的时候从后往前推即可。
另外一种思路是开一个数组记录选哪个
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int f[N][N],v[N],w[N];
int n,m;
int main()
{ cin>>n>>m;for(int i=1;i<=n;i++)cin>>v[i]>>w[i];for(int i=n;i>=1;i--){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]);}}//f[1][m]是最大值int j=m;for(int i=1;i<=n;i++){if(j>=v[i]&&f[i][j]==f[i+1][j-v[i]]+w[i])//能选就一定要选 {cout<<i<<" ";j-=v[i];}}return 0;
}
能量石
思路
讲的比较好的博客
暴力解法:把把所有全排列整出来然后每个排列做01背包取最值
这里有个问题:为啥不能直接01背包?
对于一般的选物品,无论物品如何排列,我们都可以得到相同的答案。但是本题的特殊点在于,不同顺序选的话,物品的价值是变的,前面选的物品时间越长,后面物品价值越小(甚至为0),这样DP具有后效性,没法整,出来的只是局部最优解。
这时候我们可以利用贪心缩小决策范围,将最优解的排序确定,然后进行01
贪心:
其中SiS_iSi表示前i个时间之和(前缀)
Si=t1+t2+……+tiS_i=t_1+t_2+……+t_iSi=t1+t2+……+ti
E1−L1+E2−L2∗S1+……+En−Ln∗Sn−1E_1-L_1+E_2-L_2*S_1+……+E_n-L_n*S_{n-1}E1−L1+E2−L2∗S1+……+En−Ln∗Sn−1
第i项和第i+1邻项交换
状态 | 公式 |
---|---|
交换前 | Ei−Li∗Si−1+Ei+1−Li+1∗SiE_i-L_i*S_{i-1}+E_{i+1}-L_{i+1}*S_iEi−Li∗Si−1+Ei+1−Li+1∗Si |
交换后 | Ei+1−Li+1∗Si−1+Ei−Li∗(Si−ti+ti+1)E_{i+1}-L_{i+1}*S_{i-1}+E_{i}-L_{i}*(S_i-t_i+t_{i+1})Ei+1−Li+1∗Si−1+Ei−Li∗(Si−ti+ti+1) |
比较 | Liti?Li+1ti+1\frac {L_i} {t_i}?\frac {L_{i+1}} {t_{i+1}}tiLi?ti+1Li+1 |
排序后产生另外一个问题:为啥不直接拿完最优解排序。
因为你拿一块能量石后未必会增加总能量,反而可能因为拿了这块后造成后面的能量损失过多,因此如果选择不拿的话是有可能减少这些损失的,反而有利。
DP状态转移方程
定义f[i][j]f[i][j]f[i][j]为从前i个物品中选,耗时恰好为j的所有选法集合的最大价值
f[i][j]=max(f[i−1][j],f[i−1][j−s[i]]+max(0,e[i]−l[i]∗(j−s)))f[i][j]=max(f[i-1][j],f[i-1][j-s[i]]+max(0,e[i]-l[i]*(j-s)))f[i][j]=max(f[i−1][j],f[i−1][j−s[i]]+max(0,e[i]−l[i]∗(j−s)))
代码
#include<bits/stdc++.h>
using namespace std;const int N=1e4+10;
int n;
struct Stone
{int s,e,l;bool operator<(const Stone &W)const{return s*W.l<l*W.s;}
}stone[N];
int f[N];
int main()
{int T;cin>>T;for(int C=1;C<=T;C++){int m=0;cin>>n;for(int i=0;i<n;i++){int s,e,l;cin>>s>>e>>l;stone[i]={s,e,l};m+=s;}sort(stone,stone+n);memset(f,-0x3f,sizeof f);f[0]=0;for(int i=0;i<n;i++){int s=stone[i].s,e=stone[i].e,l=stone[i].l;for(int j=m;j>=s;j--)f[j]=max(f[j],f[j-s]+e-(j-s)*l);}int res=0;for(int i=0;i<=m;i++)res=max(res,f[i]);printf("Case #%d: %d\n",C,res);}return 0;
}
金明的预算方案
思路
有依赖的分组背包问题
模型的转化
分组背包:每个主件的附件决策选法其实就是一个物品,他们是互斥的
关系的输入与存储
如何用代码实现上述这种一个连着多个而且只有一层关系的结构(附件不会成为附件的附件)
用vector数组即可,同时它仅有两种属性,那么可以用pair类型的vector存储这种依赖关系,如果是根,则存PII类型的master数组中,如果不是则存servent的vector数组。如果有多个属性的话把PII改成struct存储即可
PII master[N];
vector<PII>servent[N];
如何枚举每个选法:通过观察发现我们可以利用二进制来实现枚举例如第一张图的主2
默认初值选上主件,每个附件选或不选,二进制枚举附件选法
代码
#include<bits/stdc++.h>
using namespace std;
#define v first
#define w secondtypedef long long ll;
const int N=4e4+10;
typedef pair<int,int>PII;
ll n,m;
PII master[N];
vector<PII>servent[N];//附件
int f[N];
int main()
{cin>>m>>n;for(int i=1;i<=n;i++){int v,w,q;cin>>v>>w>>q;if(!q)master[i]={v,v*w};else servent[q].push_back({v,v*w});}for(int i=1;i<=m;i++)if(master[i].v){for(int j=m;j>=0;j--){auto &sv=servent[i];for(int k=0;k<(1<<sv.size());k++)//二进制枚举附件选法{int v=master[i].v,w=master[i].w;//默认选主件for(int u=0;u<sv.size();u++)if(k>>u&1){v+=sv[u].v;w+=sv[u].w;}if(j>=v)f[j]=max(f[j],f[j-v]+w);}}}cout<<f[m]<<endl;return 0;
}
【Acwing提高】DP·背包相关推荐
- AcWing提高算法课Level-3 第四章 高级数据结构
AcWing提高算法课Level-3 第四章 高级数据结构 并查集 AcWing 1250. 格子游戏1167人打卡 AcWing 1252. 搭配购买1064人打卡 AcWing 237. 程序自动 ...
- (背包dp) 背包N讲
文章目录 前言 相关练习题 模板题 01背包 完全背包 多重背包 小数据范围 (可朴素暴力) 中等数据范围 (二进制优化) 大数据范围 (单调队列优化) 混合背包 二维费用背包 分组背包 有依赖的背包 ...
- 二叉苹果树(树型DP+背包)
二叉苹果树 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点).这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号 ...
- AcWing提高算法课Level-3 第六章 基础算法
AcWing提高算法课Level-3 第六章 基础算法 位运算 AcWing 90. 64位整数乘法761人打卡 递推与递归 AcWing 95. 费解的开关520人打卡 AcWing 97. 约数之 ...
- LightOJ 1079 Just another Robbery (概率dp+背包)
题意:有n家银行,每家银行都有一定数量的钱和被抓概率,给出自己被抓概率的上限,求能获得最多的钱. 题解:概率dp+背包 用dp[]dp[]dp[]表示获得这么多钱被抓的概率.获得同等钱的概率要尽可能小 ...
- acwing 3 完全背包
习题地址 https://www.acwing.com/problem/content/description/3/ 题目描述 有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用. 第 i ...
- acwing提高组 第一章 动态规划
文章目录 数字三角形模型 最长上升子序列模型 背包模型 状态机模型 状态压缩DP 区间DP 树形DP 数位DP 单调队列优化DP 斜率优化DP oj链接 数字三角形模型 AcWing 1015. 摘花 ...
- Acwing提高课--数论
Acwing 数论--提高课 质数 埃氏筛: 线性筛(欧筛): 约数 求组合数 卡特兰数 欧拉函数 扩展欧几里得 同余方程组(中国剩余定理) 博弈论 容斥原理 Mobius函数 高斯消元 矩阵乘法板子 ...
- dp背包九讲(待补充,暂时前两讲)
文章目录 背包九讲 一.01背包 二维dp 优化为一维 二.完全背包 二维dp 优化为一维 三.多重背包 数据范围很小时,变成01背包暴力 当数据范围较大时,利用二进制优化 二进制优化还不够用时,利用 ...
最新文章
- 数据库设计中的9大常见错误
- 实验研究信标无线电能输出功率的因素
- python深拷贝和浅拷贝的使用场景_深拷贝、浅拷贝的理解与使用场景
- 20155327《Java程序设计》第二周学习总结
- 【FFmpeg】ffmpeg 命令查询二 ( 比特流过滤器 | 可用协议 | 过滤器 | 像素格式 | 标准声道布局 | 音频采样格式 | 颜色名称 )
- Java小结(二)——打印矩形和九九乘法表
- c语言按F1键运行,C语言的编译和运行按什么键
- IDEA Maven的下载和配置
- python3比较文本差异_脑科学方向 | Python3的安装与环境搭建
- 求逆序对(信息学奥赛一本通-T1311)
- Java面向对象编程及其三大特征
- 抖音下载android,抖音完整版
- 机器视觉开源处理库汇总
- ubuntu如何杀进程_Ubuntu下各种结束进程的方法
- 经典0-1背包问题(C++解决代码优化版本)
- 只有标准账户,如何取得管理员权限?
- poj 3904 求四元互质集合
- Linux 缓存释放和管理
- Goland Unresolved dependency问题解决
- 代码审计--49--PHP代码审计中常见的漏洞(一)
热门文章
- 给 iOS 开发者的 Flutter 指南
- 【附源码】计算机毕业设计java学习资源共享网站设计与实现
- Unity3d 防止相机“穿墙”功能
- (二)基于STM32f103的I2C通信接口的EPPROM模块(24C256)读写程序详解
- 电路之KCL和KVL的独立方程数总结
- 基于CentOs的docker的安装和简单使用
- 也谈ibm aix jfs2
- C++ std::string::find()函数(在字符串中查找内容)
- Buddy Test测试中琐事温故
- Android 输入法显示图标