7
81
将环复制一遍,就可以在2*n的序列上做DP
最容易想到的DP:
f[i][j][k]表示i——j分为k组,相乘取得的最大值
预处理:w[i][j]表示i——j相加对10取余的结果
初始化:f[i][j][1]=w[i][j]
状态转移:f[i][j][k]=f[i][l][k-1]+w[l+1][j]
g数组为最小值,处理同上
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,a[101],s[101],w[101][101],cnt;
int f[51][101][10],maxn,g[51][101][10],minn=0x7fffffff;
int main()
{
scanf("%d%d",&n,&m);
memset(g,127,sizeof(g));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=(s[i-1]+a[i])%10;
}
for(int i=1;i<n;i++) a[n+i]=a[i];
for(int i=n+1;i<2*n;i++) s[i]=s[i-1]+a[i];
for(int i=1;i<2*n;i++)
for(int j=i;j<2*n;j++)
w[i][j]=(s[j]-s[i-1]+1000000)%10;
for(int i=1;i<=n;i++)
for(int j=i;j<=i+n-1;j++)
{
f[i][j][1]=w[i][j];
g[i][j][1]=w[i][j];
}
for(int k=2;k<=m;k++)
for(int i=1;i<=n;i++)
for(int j=i;j<=i+n-1;j++)
{
if(j-i+1<k) continue;
for(int l=i;l<j;l++)
{
f[i][j][k]=max(f[i][j][k],f[i][l][k-1]*w[l+1][j]);
if(g[i][l][k-1]<2000000000)
g[i][j][k]=min(g[i][j][k],g[i][l][k-1]*w[l+1][j]);
}
}
for(int i=1;i<=n;i++)
{
maxn=max(maxn,f[i][i+n-1][m]);
minn=min(minn,g[i][i+n-1][m]);
}
cout<<minn<<endl<<maxn;
}
做的时候3个细节错误
1、w[51][101],虽然与答案有关的范围为i——i+n-1,但在状态转移中第一维的范围会超过n,因为这个错误卡了大概1小时
2、w[i][j]=(s[j]-s[i-1]+10)%10,当时想的是最终结果<10,<10的负数+10再%10就行,现在都不知道咋想的。要加一个足够大的数。
3、if(g[i][l][k-1]<2000000000) 没有加这个判断,最初g数组赋了极大值,不加判断,乘爆了
考虑优化时的错误:
发现第一维在状态转移中没有用,觉得可以压去,其余不变。
但对f数组初始化时,如果压去第一维,所有的w[i][j]都记录在了f[j][1]里,状态混乱