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