丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。
例如,对于下面这圈数字(n=4,m=2):
2
4 -1
3
当要求最小值时,((2-1) mod 10)×((4+3) mod 10)=1×7=7,要求最大值时,为((2+4+3) mod 10)×(-1 mod 10)=9×9=81。特别值得注意的是,无论是负数还是正数,对10取模的结果均为非负值。
丁丁请你编写程序帮他赢得这个游戏。
输入文件第一行有两个整数,n(1≤n≤50)和m(1≤m≤9)。以下n行每行有个整数,其绝对值不大于104,按顺序给出圈中的数字,首尾相接。
输出文件有两行,各包含一个非负整数。第一行是你程序得到的最小值,第二行是最大值。
4 2
4
3
-1
2
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]里,状态混乱