@(K ACMer)
题意:
给你一个长度为n的环,你要把它划分为m个部分,让每个部分内部元素的和取余10再相乘得到的字尽可能大(小).
分析:
如果题中是把一个链划分为m部分,显然可以用dp的方法,定义
dp[i][j]
为前i个数划分为j个部分的最大(小)值,那么可以有转移方程:
dp[i][j]=max(dp[i][j],dp[k][j−1]∗((sum[i]−sum[k])
但是是环形的,我们需要拆环为链,枚举每一个拆点,共有n条链.对每一个链来做如上dp即可.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int mod = int(1e9) + 7, INF = 1e8, maxn = 1e5 + 40;
int n, m, dp[55 * 2][11 * 2], dp1[55 * 2][11 * 2], a[55 * 2], sum[55 * 2];
int main(void) {
while (~scanf("%d%d", &n, &m)) {
memset(a, 0, sizeof(a));
memset(sum, 0, sizeof(sum));
memset(dp, 0, sizeof(dp));
memset(dp1, 0, sizeof(dp1));
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), a[i + n] = a[i];
for (int i = 1; i <= 2 * n; i++)
sum[i] += sum[i - 1] + a[i];
int ansmax = -INF, ansmin = INF;
for (int i = 0; i <= 2 * n; i++)
dp1[i][0] = dp[i][0] = 1;
for (int s = 1; s <= n; s++) {
for (int i = s; i < s + n; i++) {
for (int j = 1; j <= min(m, i - s + 1); j++) {
dp[i][j] = -INF;
dp1[i][j] = INF;
for (int k = i - 1; k - s + 1 >= j - 1; k--) {
if (j - 1 == 0 && k != s - 1) continue;
dp[i][j] = max(dp[i][j], dp[k][j - 1] * (((sum[i] - sum[k]) % 10 + 10) % 10));
dp1[i][j] = min(dp1[i][j], dp1[k][j - 1] * (((sum[i] - sum[k]) % 10 + 10) % 10));
}
}
}
/* cout << "S :" << s << endl;
for (int i = s; i < s + n; i++) {
for (int j = 1; j <= min(m, i - s + 1); j++) {
cout << dp[i][j] << " ";
}
cout << endl;
}
cout << endl;*/
ansmax = max(ansmax, dp[s + n - 1][m]);
ansmin= min(ansmin, dp1[s + n - 1][m]);
}
printf("%d\n%d\n", ansmin, ansmax);
}
return 0;
}