数字游戏 \operatorname{数字游戏} 数字游戏
题目链接: luogu P1043 \operatorname{luogu\ P1043} luogu P1043
题目
丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共 n n n 个),你要按顺序将其分为 m m m 个部分,各部分内的数字相加,相加所得的 m m m 个结果对 10 10 10 取模后再相乘,最终得到一个数 k k k 。游戏的要求是使你所得的 k k k 最大或者最小。
例如,对于下面这圈数字( n = 4 , m = 2 n=4,\ m=2 n=4, m=2 ):
要求最小值时, ( ( 2 − 1 ) m o d 10 ) × ( ( 4 + 3 ) m o d 10 ) = 1 × 7 = 7 ((2-1) \bmod 10)×((4+3) \bmod 10)=1×7=7 ((2−1)mod10)×((4+3)mod10)=1×7=7 ,要求最大值时,为 ( ( 2 + 4 + 3 ) m o d 10 ) × ( − 1 m o d 10 ) = 9 × 9 = 81 ((2+4+3) \bmod 10)×(-1 \bmod 10)=9×9=81 ((2+4+3)mod10)×(−1mod10)=9×9=81 。特别值得注意的是,无论是负数还是正数,对 10 10 10 取模的结果均为非负值。
丁丁请你编写程序帮他赢得这个游戏。
输入
输入文件第一行有两个整数, n ( 1 ≤ n ≤ 50 ) n(1≤n≤50) n(1≤n≤50) 和 m ( 1 ≤ m ≤ 9 ) m(1≤m≤9) m(1≤m≤9) 。以下 n n n 行每行有个整数,其绝对值 $\le 10^4 ,按顺序给出圈中的数字,首尾相接。
输出
输出文件有 2 2 2 行,各包含 1 1 1 个非负整数。第 1 1 1 行是你程序得到的最小值,第 2 2 2 行是最大值。
样例输入
4 2
4
3
-1
2
样例输出
7
81
思路
这道题是一道 dp 。
我们先看求最大:设
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k] 为第
i
i
i 个数到第
j
j
j 个数,分成
k
k
k 个部分最大的
k
k
k 是多少。那就是从分成
k
−
1
k - 1
k−1 和一个部分合在一起转移过来。那就枚举四重循环,前三个就是
i
j
k
ijk
ijk ,在枚举一个表示从哪里割开两半。
那我们可以看出当第三维是
1
1
1 的时候,是转移不了的,那就要用初始化。因为只分一份,就直接相加取模,而相加可以用前缀和来所短时间。
而找到最佳答案时,要枚举每一种割开环的情况,找到最大的答案。
求最小的同理。
要注意的就是题目负数的取模不一样,正常取模之后加 10 10 10 在取模一次就可以了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
ll n, m, a[101], minn[101][101][10], maxn[101][101][10], q[101], maxans, minans = 10000000000000000;
int main() {
for (int i = 1; i <= 100; i++)
for (int j = 1; j <= 100; j++)
for (int k = 1; k <= 10;k++)
minn[i][j][k] = 1000000000000;//初始化
scanf("%lld %lld", &n, &m);//读入
for (ll i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
a[i + n] = a[i];//把环切开
q[i] = q[i - 1] + a[i];//前缀和
}
for (ll i = n + 1; i <= 2 * n; i++)
q[i] = q[i - 1] + a[i];//前缀和
for (ll i = 1; i <= n * 2; i++)
for (ll j = 1; j <= n * 2; j++)
maxn[i][j][1] = minn[i][j][1] = ((q[j] - q[i - 1]) % 10 + 10) % 10;//预处理
for (ll i = 1; i <= n * 2; i++)
for (ll j = i + 1; j <= n * 2; j++)
for (ll k = 2; k <= m; k++)
for (ll l = i; l < j; l++) {//dp
maxn[i][j][k] = max(maxn[i][j][k], maxn[i][l][k - 1] * maxn[l + 1][j][1]);
minn[i][j][k] = min(minn[i][j][k], minn[i][l][k - 1] * minn[l + 1][j][1]);
}
for (ll i = 1; i <= n; i++) {//找到最好的起点
maxans = max(maxans, maxn[i][i + n - 1][m]);
minans = min(minans, minn[i][i + n - 1][m]);
}
printf("%lld\n%lld", minans, maxans);//输出
return 0;
}