AcWing算法提高课 区间dp / 通过取模将数据规整到某区间 / 环形dp处理
区间dp常用的实现方式有:迭代式和记忆化搜索式。
迭代式可以枚举左右端点,也可以考虑先枚举区间长度,这样小区间就会在大区间之前被计算。大区间通过分界点由小区间转移。
例题
AcWing 282. 石子合并
此题直接枚举区间长度和左端点,右端点不超过n。
AcWing 1068. 环形石子合并
此题可以考虑枚举区间长度和左端点,由于是环形数组,右端点可以越界,通过取模可以找到正确位置。
注意在枚举右端点和枚举中间点时均需要判断是否越界,如果越界需要取模。
如果区间为0-9,对num处理越界可以num%10;如果区间为1-9,对num处理越界可以(num-1)%9+1;
区间为m-n 可以(num-m)%(n-m+1)+m;
区间长度是多少就对多少取模,且区间右端点恰好不被取模影响,最后加上偏移。

#include<bits/stdc++.h> using namespace std; //0-9可以mod10,1-9可以(i-1)%9+1 int nums[210]; int dp[210][210]; int psum[210]; int main() { int n; cin>>n; for(int i=1;i<=n;i++) cin>>nums[i]; for(int i=1;i<=n;i++) psum[i]=psum[i-1]+nums[i]; memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][i]=0; for(int len=2;len<=n;len++) { for(int i=1;i<=n;i++) { int j=i+len-1; int cpsum; if(j<=n) { cpsum=psum[j]-psum[i-1]; } else { cpsum=psum[n]-psum[i-1]+psum[(j-1)%n+1]; } for(int k=i;k<j;k++) { dp[i][(j-1)%n+1]=min(dp[i][(j-1)%n+1],dp[i][(k-1)%n+1]+dp[(k)%n+1][(j-1)%n+1]+cpsum); } } } int res=dp[1][n]; for(int i=2;i<=n;i++) res=min(res,dp[i][i-1]); cout<<res<<endl; memset(dp,0,sizeof(dp)); for(int len=2;len<=n;len++) { for(int i=1;i<=n;i++) { int j=i+len-1; int cpsum; if(j<=n) { cpsum=psum[j]-psum[i-1]; } else { cpsum=psum[n]-psum[i-1]+psum[(j-1)%n+1]; } for(int k=i;k<j;k++) { dp[i][(j-1)%n+1]=max(dp[i][(j-1)%n+1],dp[i][(k-1)%n+1]+dp[(k)%n+1][(j-1)%n+1]+cpsum); } } } res=dp[1][n]; for(int i=2;i<=n;i++) res=max(res,dp[i][i-1]); cout<<res<<endl; }
或者可以考虑在原数组尾部再接一个原数组,获得长度为2n的数组,并用282石子合并的方法求解。这也是环形dp的常用解法。
AcWing 1069. 凸多边形的划分 (此题还需要结合高精度,或者用int128,或者用python)
此题可以转化为与282类似的区间合并问题。因为三角形之间不会相交,可以枚举区间左右端点以及中间点所成的三角形进行区间dp。

#include<bits/stdc++.h> using namespace std; typedef long long LL; const int M=35; LL dp[55][55][35]; LL nums[55]; void add(LL a[],LL b[]) { static LL c[M]; memset(c,0,sizeof(c)); LL carry=0; for(int i=0;i<M;i++) { carry+=a[i]+b[i]; c[i]=carry%10; carry/=10; } memcpy(a,c,sizeof(c)); } void mul(LL a[],LL b) { static LL c[M]; memset(c,0,sizeof(c)); LL carry=0; for(int i=0;i<M;i++) { carry+=a[i]*b; c[i]=carry%10; carry/=10; } memcpy(a,c,sizeof(c)); } bool cmp(LL a[],LL b[]) { for(int i=M-1;i>=0;i--) { if(a[i]<b[i]) return true; if(a[i]>b[i]) return false; } return false; } int main() { int n; cin>>n; for(int i=1;i<=n;i++) cin>>nums[i]; LL tmp[M]; for(int len=3;len<=n;len++) { for(int l=1;l+len-1<=n;l++) { int r=l+len-1; dp[l][r][M-1]=1; for(int k=l+1;k<r;k++) { memset(tmp,0,sizeof(tmp)); tmp[0]=1; mul(tmp,nums[l]); mul(tmp,nums[k]); mul(tmp,nums[r]); add(tmp,dp[l][k]); add(tmp,dp[k][r]); if(cmp(tmp,dp[l][r])) { memcpy(dp[l][r],tmp,sizeof(tmp)); } //dp[l][r]=min(dp[l][r],dp[l][k]+dp[k][r]+nums[l]*nums[k]*nums[r]); } } } int s=M-1; while(s>=0&&dp[1][n][s]==0) s--; for(int i=s;i>=0;i--) cout<<dp[1][n][i]; cout<<endl; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人