Living-Dream 系列笔记 第78期
常用 dp 状态:\(dp_i\) 表示以 \(i\) 结尾的 XXX / 前 \(i\) 个元素的 XXX。
涉及的类型(由易到难):线性 dp,背包,区间 dp,树形 dp(换根 dp),状压 dp,dp 的各类优化(数据结构优化、斜率优化、四边形不等式优化......)。
背包问题:01 背包、完全背包、分组背包、多重背包、树上背包。
P1455
并查集维护配套,按照所有套餐进行 01 背包即可。
总结:本题是并查集与 01 背包的结合,具有综合性。
code
#include<bits/stdc++.h> using namespace std; const int N=1e4+5; int n,m,w; int c[N],d[N]; int p[N],fa[N],dp[N]; int fnd(int x){ return (fa[x]==x?x:fa[x]=fnd(fa[x])); } void uni(int x,int y){ x=fnd(x),y=fnd(y); if(x!=y){ fa[x]=y; c[y]+=c[x]; d[y]+=d[x]; } } int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>m>>w; for(int i=1;i<=n;i++) cin>>c[i]>>d[i],fa[i]=i; for(int i=1,u,v;i<=m;i++) cin>>u>>v,uni(u,v); int cnt=0; for(int i=1;i<=n;i++){ if(fa[i]==i) p[++cnt]=i; } for(int i=1;i<=cnt;i++) for(int j=w;j>=c[p[i]];j--) dp[j]=max(dp[j],dp[j-c[p[i]]]+d[p[i]]); cout<<dp[w]; return 0; }
CF507A
定义 \(path_{i,j}\) 记录背包容量为 \(j\) 时物品 \(i\) 是否转移成功,每次转移时标记即可。
最后递归地寻找答案,具体见代码。
总结:本题是记录决策点的 01 背包,以后要求记录决策点的 dp 都可以仿照着这样写。
code
#include<bits/stdc++.h> using namespace std; const int N=1e2+5,K=1e4+5; int n,k,cnt; int c[N],dp[K],ans[N]; bool path[N][K]; void getans(int cur,int l){ if(!cur) return; if(path[cur][l]){ ans[++cnt]=cur; getans(cur-1,l-c[cur]); } else getans(cur-1,l); } int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>k; for(int i=1;i<=n;i++) cin>>c[i]; for(int i=1;i<=n;i++){ for(int j=k;j>=c[i];j--){ if(dp[j]<dp[j-c[i]]+1){ dp[j]=dp[j-c[i]]+1; path[i][j]=1; } } } getans(n,k); cout<<cnt<<'\n'; for(int i=1;i<=cnt;i++) cout<<ans[i]<<' '; return 0; }
AT_abc054_d
既然有两种类型的物品,我们就设两维,令 \(dp_{i,j}\) 表示选重量为 \(i\) 的 A 元素且选重量为 \(j\) 的 B 元素所能获得的最大价值。仍然像 01 背包一样转移即可。
最后,我们枚举所有状态,对于所有满足要求的状态取最小值即可。
总结:本题是具有多个维度的 01 背包。谨记:当存在多个物品时,考虑增设维度。
code
#include<bits/stdc++.h> using namespace std; const int N=5e2+5; int n,ma,mb,sa,sb; int a[N],b[N],c[N]; int dp[N][N]; int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>ma>>mb; for(int i=1;i<=n;i++){ cin>>a[i]>>b[i]>>c[i]; sa+=a[i],sb+=b[i]; } memset(dp,0x3f,sizeof dp); dp[0][0]=0; for(int i=1;i<=n;i++) for(int j=sa;j>=a[i];j--) for(int k=sb;k>=b[i];k--) dp[j][k]=min(dp[j][k],dp[j-a[i]][k-b[i]]+c[i]); int ans=1e9; for(int i=1;i<=sa;i++) for(int j=1;j<=sb;j++) if(j*ma==i*mb) ans=min(ans,dp[i][j]); cout<<(ans==1e9?-1:ans); return 0; }
AT_abc118_d
首先这显然是个完全背包,我们先按照完全背包的方式做,令 \(dp_i\) 表示用 \(i\) 根火柴能拼成的最大位数。
显然,位数越大越好,位数相同的情况下数码越大越好,所以我们将 \(a\) 先排个序。
然后从 \(n\) 开始(这样保证了位数最大),每次在 \(m\) 个数码中选取能拼成 \(dp_n\) 这么多位且当前这一位为 \(a_i\) 的最大的那个 \(a_i\),记录下它即可。
总结:本题是完全背包与贪心的结合,这启发我们可以在 dp 之后贪心选取最优方案,这两个东西并不是水火不容。
code
#include<bits/stdc++.h> using namespace std; const int N=1e4+5; const int num[]={0,2,5,5,4,5,6,3,7,6}; int n,m,tot; int a[N],dp[N],ans[N]; int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>m; for(int i=1;i<=m;i++) cin>>a[i]; memset(dp,0xcf,sizeof dp); dp[0]=0; for(int i=1;i<=m;i++) for(int j=num[a[i]];j<=n;j++) dp[j]=max(dp[j],dp[j-num[a[i]]]+1); //int cur=dp[n]; sort(a+1,a+m+1); while(n){ //cout<<cur<<'\n'; int p=0; for(int i=1;i<=m;i++) if(n>=num[a[i]]&&dp[n-num[a[i]]]+1==dp[n]) p=i; n-=num[a[p]]; ans[++tot]=a[p]; } for(int i=1;i<=tot;i++) cout<<ans[i]; return 0; }
CF632E
正解是 FFT / NTT,但是朴素完全背包也可以过。
这题并不要求最大价值,于是我们换一种状态定义:令 \(dp_i\) 表示凑成价值为 \(i\) 需要的最少商品数,转移 \(dp_i=\min(dp_i,dp_{i-a_j}+1)\),这样方便统计答案。
但是题目需要恰好取 \(k\) 件商品,于是我们可以将每个物品的价值全都减去最小的那个商品的价值,这样任意商品数 \(\le k\) 的方案都可以通过不断加上最小的那个商品从而凑成 \(k\) 个,之后在加回去就可以了。
总结:本题是完全背包的变种,且用到了一个小技巧。我们必须要依据题目关心的东西来设计状态。
code
#include<bits/stdc++.h> using namespace std; const int N=1e6+5; int n,k,mx,mn=1e9; int a[N],dp[N]; int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>k; for(int i=1;i<=n;i++){ cin>>a[i]; mn=min(mn,a[i]); mx=max(mx,a[i]); } mx-=mn; for(int i=1;i<=n;i++) a[i]-=mn; memset(dp,0x3f,sizeof dp); dp[0]=0; for(int i=0;i<=k*mx;i++) for(int j=1;j<=n;j++) if(i>=a[j]) dp[i]=min(dp[i],dp[i-a[j]]+1); for(int i=0;i<=k*mx;i++) if(dp[i]<=k) cout<<i+k*mn<<' '; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】