Living-Dream 系列笔记 第49期
T1
令 \(dp_{i,j}\) 表示卖出区间 \([i,j]\) 能获得的最大价值。
显然答案为 \(dp_{1,n}\)。
因为只能卖 \(i\) / \(j\),所以有转移:
初始:\(dp_{i,i}=v_i \times n\),其余为 \(- \infty\)。
code
#include<bits/stdc++.h> using namespace std; const int N=2e3+5; int n; int v[N]; int dp[N][N]; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>v[i]; memset(dp,0xcf,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][i]=v[i]*n; for(int i=2;i<=n;i++){ for(int j=1;j+i-1<=n;j++){ int s=j,e=j+i-1; dp[s][e]=max(dp[s][e-1]+v[e]*(n-i+1),dp[s+1][e]+v[s]*(n-i+1)); } } cout<<dp[1][n]; return 0; }
T2
我们注意到,对于字符串中的一个非对称位置 \(i\),
在对应位置添加 \(s_i\) 或删除 \(s_i\) 是等价的。
于是对于每个字符,它的操作代价即为 \(\min(\)添加代价\(,\)删除代价\()\)。
然后这题显然是个区间 dp。
因此有状态:令 \(dp_{i,j}\) 表示 将 \([i,j]\) 改为回文串的最小代价,答案为 \(dp_{1,m}\)。
接着我们发现这题显然不能用枚举中转点的套路,
因为即使 \([i,k]\) 与 \([k+1,j]\) 均为回文串,也不能保证 \([i,j]\) 为回文串。
于是我们考虑对端点进行转移,
具体来说,我们有:
因为我们是从短到长枚举区间,所以此转移的正确性可以保证。
同时,若 \(s_i=s_j\),则继承中间的 \(dp_{i+1,j-1}\) 即可。
在这种情况下,若区间长度 \(i \le 2\),则直接令 \(dp_{i,j}=0\) 即可。
code
#include<bits/stdc++.h> using namespace std; const int N=2e3+5; int n,m; string t; int v[N]; int dp[N][N]; int main(){ cin>>n>>m>>t,t='#'+t; for(int i=1;i<=n;i++){ char c; int x,y; cin>>c>>x>>y; v[c]=min(x,y); } memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=m;i++) dp[i][i]=0; for(int i=2;i<=m;i++){ for(int j=1;j+i-1<=m;j++){ int s=j,e=j+i-1; if(t[s]==t[e]){ if(i==2) dp[s][e]=0; else dp[s][e]=dp[s+1][e-1]; } dp[s][e]=min(dp[s][e],min(dp[s][e-1]+v[t[e]],dp[s+1][e]+v[t[s]])); } } cout<<dp[1][m]; return 0; }
T3
与上题类似,此处略。
T4
一眼区间 dp。
令 \(dp_{i,j}\) 表示 \([i,j]\) 能折叠出的最小长度。
答案即为 \(dp_{1,n}\)(\(n\) 表示 \(S\) 的长度)。
初始状态显然有 \(dp_{i,i}=1\),其余为 \(\infty\)。
对于转移,我们进行分类讨论:
- 若不折叠,直接拼接,则按照枚举中转点套路即可。有转移:
-
否则若进行折叠,则重复部分的长度必为当前区间长度 \(len\) 的因数。
于是我们从 \(1\) 至 \(\frac{len}{2}\)(因为枚举到 \(len\) 就相当于情况 1)去枚举 \(len\) 的因数 \(l\),
并检查其是否满足要求。若满足,则有转移:
(其中 \(c(\frac{len}{l})\) 表示 \(\frac{len}{l}\) 的位数,即题中的 \(X\);\(2\) 表示两个括弧)
直接转移即可。
code
#include<bits/stdc++.h> using namespace std; const int N=1e2+5; int m; string t; int v[N]; int dp[N][N]; bool check(int l,int r,int k){ string o=""; for(int i=l;i<=r;i+=k){ if(i==l) o=t.substr(i,k); else if(t.substr(i,k)!=o) return 0; } return 1; } int c(int x){ int p=0; while(x) p++,x/=10; return p; } int main(){ cin>>t,t='#'+t,m=t.size()-1; memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=m;i++) dp[i][i]=1; for(int i=2;i<=m;i++){ for(int j=1;j+i-1<=m;j++){ int s=j,e=j+i-1; for(int k=s;k<e;k++) dp[s][e]=min(dp[s][e],dp[s][k]+dp[k+1][e]); for(int l=1;l<=i/2;l++) if(!(i%l)&&check(s,e,l)) dp[s][e]=min(dp[s][e],dp[s][s+l-1]+c(i/l)+2); } } cout<<dp[1][m]; return 0; }
作业 T1
令 \(dp_{i,j}\) 表示删去区间 \([i,j]\) 能获得的最大价值,答案即为 \(dp_{1,n}\)。
显然有初始状态令 \(dp_{i,i}=x_i\),其余为 \(-\infty\)。
对于 \([i,j]\),我们可以直接全删,于是先令 \(dp_{i,j}=\lvert x_i-x_j \rvert \times (j-i+1)\)。
然后按枚举中转点套路转移即可。
code
#include<bits/stdc++.h> using namespace std; const int N=1e2+5; int n,a[N],dp[N][N]; int main(){ cin>>n,memset(dp,0xcf,sizeof(dp)); for(int i=1;i<=n;i++) cin>>a[i],dp[i][i]=a[i]; for(int i=2;i<=n;i++){ for(int j=1;j+i-1<=n;j++){ int s=j,e=j+i-1; dp[s][e]=abs(a[s]-a[e])*(e-s+1); for(int k=s;k<e;k++) dp[s][e]=max(dp[s][e],dp[s][k]+dp[k+1][e]); } } cout<<dp[1][n]; return 0; }
作业 T2
因为题中只含有两端点操作,所以考虑对端点进行转移。
具体地:
令 \(dp_{i,j,0}\) 表示区间 \([i,j]\) 中 \(i\) 从左边进的的初始队列方案数;
令 \(dp_{i,j,0}\) 表示区间 \([i,j]\) 中 \(j\) 从右边进的的初始队列方案数。
答案即为 \((dp_{1,n,0}+dp_{1,n,1}) \bmod 19650827\)。
然后显然有初始状态令 \(dp_{i,i,0}=1\),
这里默认第一个人从左进,避免重复计算。
接着考虑到第 \(i\) 人从左进时,它前面的人只可能为 \(i+1\) 或 \(j\),
因此它必须满足 \(h_i<h_{i+1}\) 或 \(h_i<h_j\) 才能进行转移,
易得此时转移方程:
同理,易得第 \(j\) 人从右进时的转移方程:
然后直接转移即可。注意取模。
code
#include<bits/stdc++.h> using namespace std; const int N=1e3+5,MOD=19650827; int n,a[N],dp[N][N][2]; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i],dp[i][i][0]=1; for(int i=2;i<=n;i++){ for(int j=1;j+i-1<=n;j++){ int s=j,e=j+i-1; if(a[s]<a[s+1]) dp[s][e][0]+=dp[s+1][e][0]; if(a[s]<a[e]) dp[s][e][0]+=dp[s+1][e][1]; if(a[e]>a[e-1]) dp[s][e][1]+=dp[s][e-1][1]; if(a[e]>a[s]) dp[s][e][1]+=dp[s][e-1][0]; dp[s][e][0]%=MOD,dp[s][e][1]%=MOD; } } cout<<(dp[1][n][0]+dp[1][n][1])%MOD; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效