思路
1.首先我们要先解决环形问题:一般有两种办法:
1.1对于普通的遍历来说只要i%len(len为长度)就可以转换索引了
1.2直接用两个区间装圈中的值,两个区间完全相等,然后连在一起,例如该题 -1 3 4 2 -1 3 4 2 ,这样就可以变成用取长度为4的起点不同的区间来包含所有的圈中的情况了
2.进过第一步,我们就把问题转化成了把一个区间长度为n的区间分成m份然后计算的题了,这题用dp,令f[i][j][k]表示在区间i~j分k份的最小值,我们模拟最后一步,也就是枚举最后一份是从哪里开始的,状态转移方程:f[i][j][k]=min(f[i][t][k-1],f[t+1][j])(其中t取i+k-2 ~ j-1,因为要取k-1分,那么t-i+1>=k-1,结尾最大到j-1).
3。预处理,由于计算过程用到了前缀和,所以这里与处理一下
4.注意枚举的顺序,是区间依赖,所以要先枚举小区间,然后枚举起点,在就是从大到小枚举k的值,也是一样是因为依赖问题,大的k依赖小的k,最后是t,也就是中间划分的点
5.初始化问题,求最大值要初始化-INF,最小值相反。
代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=110,M=10,INF = 0x3f3f3f3f;
int f[N][N][M];
int g[N][N][M];
int w[N];
int s[N];
int getwork(int i){
return (i % 10 + 10) % 10;
}
int main(){
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> w[i];
w[i+n]=w[i];
}
for(int i=1;i<=2*n;i++){
s[i]=s[i-1]+w[i];
}
//f[l][r][k]=min(f[l][j][k-1]*f[j-1][r][1])
memset(f,0x3f,sizeof f);
memset(g,-0x3f,sizeof g);
for(int len=1;len<=n;len++){
for(int i=1;i+len-1<=n*2;i++){
int l=i;
int r=i+len-1;
f[l][r][1]=getwork(s[r]-s[l-1]);
g[l][r][1]=getwork(s[r]-s[l-1]);
for(int k=2;k<=m;k++){
for(int j=i+k-2;j<=r-1;j++){
f[l][r][k] = min(f[l][r][k], f[l][j][k - 1] * getwork(s[r] - s[j]));
g[l][r][k] = max(g[l][r][k], g[l][j][k - 1] * getwork(s[r] - s[j]));
}
}
}
}
int maxn=-INF;
int minn=INF;
for(int i=1;i<=n;i++){
maxn=max(maxn,g[i][i+n-1][m]);
minn=min(minn,f[i][i+n-1][m]);
}
cout << minn << "\n" << maxn;
}