如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这N个部分的乘积最
大。N、M从键盘输入,输出最大值及一种划分方式。
输入数据:
第一行一个正整数T(T<=10000),表示有T 组数据。
接下来T行每行两个正整数N,M。
输出数据:
对于每组数据
第一行输出最大值。
第二行输出划分方案,将N 按顺序分成M个数输出,两个数之间用空格格开。
样例
输入文件:separate.in
1
199 2
输出文件:separate.out
171
19 9
方法很簡單,有一些注意事項。狀態:用f[i][j]表示將前i位數分成j個部分的最大乘積。
轉移方程:f[i][j] = max(f[k - 1][j - 1] * c[k][i])。(一定要注意k的範圍是j <= k <= i,調試的時候就因為這個錯誤調了半天。)
每次數組要清零,並且f數組最開始最好賦為-1,使f數組的值必有改變,保證記錄方案的g數組能記錄到相應的k值。
注意數據類型是long long,特別是輸出的時候。
ACCode:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>
using std::max;
typedef long long LL;
const char fi[] = "separate.in";
const char fo[] = "separate.out";
const int maxN = 30;
const int MAX = 0x3fffff00;
const int MIN = -MAX;
LL f[maxN][maxN];
int g[maxN][maxN];
LL c[maxN][maxN];
int n, m, T;
LL N;
void init_file()
{
freopen(fi, "r", stdin);
freopen(fo, "w", stdout);
}
void print(int i, int j)
{
if (i <= 0 || j <= 0) return;
print(g[i][j] - 1, j - 1);
printf("%I64d ", c[g[i][j]][i]);
}
void work()
{
scanf("%d", &T);
for (int t = 0; t < T; ++t)
{
memset(f, 0xff, sizeof(f));
memset(g, 0, sizeof(g));
memset(c, 0, sizeof(c));
scanf("%I64d%d", &N, &m);
n = 0;
for (LL tmp = N; tmp; tmp /= 10) ++n;
for (int i = n; i > 0; --i)
{c[i][i] = N % 10; N /= 10; }
for (int i = 1; i < n + 1; ++i)
for (int j = i + 1; j < n + 1; ++j)
c[i][j] = c[i][j - 1] * 10 + c[j][j];
for (int i = 1; i < n + 1; ++i)
{f[i][1] = c[1][i]; g[i][1] = 1; }
for (int i = 2; i < n + 1; ++i)
for (int j = 2; j < i + 1; ++j)
for (int k = j; k < i + 1; ++k)
//注意k的枚舉範圍!!
if (f[k - 1][j - 1] * c[k][i] > f[i][j])
{
f[i][j] = f[k - 1][j - 1] * c[k][i];
g[i][j] = k;
}
printf("%I64d\n", f[n][m]);
print(n, m);
printf("\n");
}
}
int main()
{
init_file();
work();
exit(0);
}