1 条题解
-
0
思路与算法
把圆环复制一遍,枚举切开起点。对长n的一段线性序列做恰m段划分,分别维护前i个数分g段的最小/最大乘积。枚举最后一段起点j,段值为规范化到0..9的前缀差模10,乘上一层值。所有切开起点中再取全局极值。
正确性说明
任意圆环m段划分都可以从某条切边展开,枚举起点不会遗漏。固定展开后,最后一段划界j唯一,前部必须是g-1个非空段。每段余数非负,乘同一个段值保持前部最小与最大次序,故两套极值转移分别准确。初态空序列0段乘积为1,保证首段值直接进入。
复杂度
O(mn³)时间、O(mn)空间,含每个起点的前缀和、状态表初始化;n≤50,m≤9。
易错点
负数求模必须加10规范化。段不能为空;零余数合法,最小乘积常为0。最大值≤9^9,使用64位。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<int> a(2 * n); for (int i = 0; i < n; i++) { cin >> a[i]; a[i + n] = a[i]; } long long lo = 1LL << 60, hi = 0; for (int start = 0; start < n; start++) { vector<int> pre(n + 1); for (int i = 0; i < n; i++) pre[i + 1] = pre[i] + a[start + i]; vector<vector<long long>> mn(m + 1, vector<long long>(n + 1, 1LL << 60)), mx(m + 1, vector<long long>(n + 1, -1)); mn[0][0] = mx[0][0] = 1; for (int g = 1; g <= m; g++) for (int i = g; i <= n; i++) for (int j = g - 1; j < i; j++) if (mx[g - 1][j] >= 0) { int v = ((pre[i] - pre[j]) % 10 + 10) % 10; mn[g][i] = min(mn[g][i], mn[g - 1][j] * v); mx[g][i] = max(mx[g][i], mx[g - 1][j] * v); } lo = min(lo, mn[m][n]); hi = max(hi, mx[m][n]); } cout << lo << "\n" << hi << "\n"; }
- 1