P1043题解
题目链接
首先我们大致计算发现暴搜会原地爆炸,所以我们考虑使用区间DP.我们注意到题干里提到过分段数这个概念,所以在普通的区间DP的基础上我们应该再加一维进行枚举.这里我们注意题目是一个环,所以我们要断环成链双倍链.
我们定义dp[i][j][l]表示i到j这个区间分成l段的最大值或最小值.我们如何转移呢?我们给出一下式子:
这里的k表示将大区间切成l块后最后一块与倒数第二块的交界处.l是不可能多于区间长度的,这里要注意. 那么我们可以得到答案为
最小值以此类推.为什么状态转移方程写成这样?主要是因为我们不想枚举长度和左端点,因此枚举了左端点和右端点,可这样有时子区间的值还没有更新就要维护大区间,这样必定会出错.我们这样写转移方程就只需维护被分成1段的情况.那么分成一段我们只需使用前缀和维护即可.
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=205,mod=10;
int n,m,a[N],dp[N][N][N],s[N],dp1[N][N][N],ma,mi=(1e9);
signed main(){memset(dp1,0x3f,sizeof(dp1));cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i],a[i]=(a[i]+10000)%mod,a[i+n]=a[i];for(int i=1;i<=2*n-1;i++){s[i]=s[i-1]+a[i],s[i]%=mod;}for(int i=1;i<=2*n-1;i++){for(int j=i;j<=2*n-1;j++){dp[i][j][1]=(s[j]-s[i-1]+10000)%mod;dp1[i][j][1]=(s[j]-s[i-1]+10000)%mod;}}for(int i=1;i<=n*2-1;i++){for(int j=i;j<=n*2-1;j++){for(int l=2;l<=min(j-i+1,m);l++){for(int k=i;k<j;k++){dp[i][j][l]=max(dp[i][j][l],dp[i][k][l-1]*dp[k+1][j][1]);dp1[i][j][l]=min(dp1[i][j][l],dp1[i][k][l-1]*dp1[k+1][j][1]);}}}}for(int i=1;i<=n;i++){ma=max(ma,dp[i][i+n-1][m]);mi=min(mi,dp1[i][i+n-1][m]);}cout<<max(mi,0ll)<<endl<<ma;return 0;
}