(Day13) 算法复健运动for蓝桥杯-背包
(Day13) 算法复健运动for蓝桥杯-背包
01背包:
https://www.acwing.com/problem/content/2/
倒着循环,保证每个只用一次
for(int i=1;i<=n;i++)
{
cin>>v>>w;
for(int j=t;j>=v;j--)
{
dp[j]=max(dp[j],dp[j-v]+w);
}
}
换容量的01背包:
见动态规划笔记本里面有篇叫“改容量的01背包”
完全背包:
https://www.acwing.com/problem/content/3/
正着循环,每个可以使用多次
for(int i=1;i<=n;i++)
{
cin>>v>>w;
for(int j=v;j<=t;j++)
{
dp[j]=max(dp[j],dp[j-v]+w);
}
}
多重背包:
https://www.acwing.com/problem/content/6/
每个物品有他的数量,
普通的多重背包:
#include<bits/stdc++.h>
using namespace std;
const int N=109;
int v[N];//体积
int w[N];//价值
int s[N];//数量
int dp[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);//种数和容积
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&v[i],&w[i],&s[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)
{
for(int k=0;k<=s[i]&&k*v[i]<=j;k++)
{
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
}
}
}
printf("%d\n",dp[m]);
return 0;
}
二进制优化的多重背包:
原理:生成的每个k和剩下的s[i]可以用来表示个数范围中的所有个数,所以用这些数去跑多重背包会减少时间。
#include<bits/stdc++.h>
using namespace std;
const int N=2009;
int v[N];//体积
int w[N];//价值
int s[N];//数量
int dp[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);//种数和容积
for(int i=1; i<=n; i++)
{
scanf("%d%d%d",&v[i],&w[i],&s[i]);
}
for(int i=1; i<=n; i++)
{
for(int k=1; k<=s[i]; k*=2)
{
s[i]-=k;
for(int j=m;j>=k*v[i];j--)
{
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
}
}
if(s[i]>0)
{
for(int j=m;j>=s[i]*v[i];j--)
{
dp[j]=max(dp[j],dp[j-s[i]*v[i]]+s[i]*w[i]);
}
}
}
printf("%d\n",dp[m]);
return 0;
}
单调队列优化的多重背包:
#include<bits/stdc++.h>
using namespace std;
const int N=20009;
int n,m;
int f[N],g[N],q[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
int v,w,s;
scanf("%d%d%d",&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)
{
f[k]=g[k];
if(hh<=tt&&k-s*v>q[hh])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;
}
}
}
printf("%d\n",f[m]);
return 0;
}
例题:luogu P1504 积木城堡
https://www.luogu.com.cn/problem/P1504
首先得理解题意,已有的积木不能重新组合,必须只能抽出来。
思路是去单独把每一个城堡跑一次dp,看看哪些高度能组出来,vis[i]为01数组表示高度为i的城堡是否能组出来,然后把每个城堡的状态加起来,如果dp[j]==n,说明这个高度每个城堡都可以组成,所以从大往小遍历第一个=n的就满足,为了顺应题目要求vis[0]=1。遍历必须遍历到0
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+9;
int n,x;
int dp[N],vis[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
vector<int>ve;
while(cin>>x&&x!=-1)
{
ve.push_back(x);
}
int len=ve.size();
vis[0]=1;
for(int j=0;j<len;j++)
{
for(int k=N;k>=ve[j];k--)
{
if(vis[k-ve[j]])vis[k]=1;
}
}
for(int j=0;j<=N;j++)
{
dp[j]+=vis[j];
}
}
for(int i=N;i>=0;i--)
{
if(dp[i]==n)
{
cout<<i<<endl;
break;
}
}
return 0;
}
分类:
ACM / 动态规划
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义