[DP] 整数划分 题解

题目描述

如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这M个部分的乘积最大。N、M从键盘输入,输出最大值及一种划分方式。

输入格式

第一行一个正整数T(T<=10000),表示有T组数据。

接下来T行每行两个正整数N,M。

输出格式

对于每组数据

第一行输出最大值。

第二行输出划分方案,将N按顺序分成M个数输出,两个数之间用空格格开。

样例

样例输入

1
199 2

样例输出

171
19 9

题解

拿到题第一眼,这不典型的DFS吗;

看了眼数据范围。。。

果断放弃DFS;

仔细读题,发现有“划分次数”这样带有明显阶段性的词语,很难不让人想到DP;

将一个整数划分成一个个区间,这提示了我们DP的种类:区间DP(其实种类知道不知道都一样);

定义f[i][j]表示前i位截了j次所能达到的最大价值;

初始化$$f[i][1] = a[1][i]$$其中a[i][j]表示从第i位到第j位的数;

例如,对于数12345,a[3][5] == 345;

状态转移方程$$f[i][j] = max(f[i][j], f[k][j - 1] * a[k + 1][i])$$ 其中k为断点,需要从1到i - 1枚举(很简单,不再详细解释);

我们发现,a数组需要预处理出来,其实可以采用区间DP的思想,在这里我采用的是一个非常麻烦的个人思路,但也能出来正确结果;

接下来就是输出路径的问题了;

定义g[i][j]表示前i位划分j次如果要达到最大值需要的断点,每次更新f数组时顺便将g数组更新;

输出时,我们只需从m开始倒序遍历,每划分一次就输出一次,同时更新总长度的值(因为已经输出的值对于我们来说是无用的),这样就可以输出路径;

具体看代码

代码

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
long long n, m;
int t;
long long a[1005][1005];
long long f[1005][1005]; //长度,次数; 
long long g[1005][1005];
int b[105];
int c[105];
int main() {
	cin >> t;
	for (int pp = 1; pp <= t; pp++) {
		cin >> n >> m;
		memset(a, 0, sizeof(a));
		memset(f, 0, sizeof(f));
		memset(b, 0, sizeof(b));
		memset(c, 0, sizeof(c));
		memset(g, 0, sizeof(g));
		long long o = 1; //输入的数的总位数
		while(n) {
			b[o] = n % 10;
			n /= 10;
			o++;
		}
		o--;
		for (int i = 1; i <= o; i++) {
			c[i] = b[o - i + 1];
		}
		for (int i = 1; i <= o; i++) {
			for (int j = 1; j <= o; j++) {
				int p = i;
				for (int k = j - i + 1; k >= 1; k--) {
					a[i][j] += (pow(10, k)) * c[p];
					p++;
				}
				a[i][j] /= 10;
			}
		} //上面都是预处理数组a;
		for (int i = 1; i <= o; i++) {
			f[i][1] = a[1][i]; //初始化;
		}
		for (int j = 2; j <= m; j++) {
			for (int i = 1; i <= o; i++) {
				for (int k = 1; k < i; k++) {
					if (f[i][j] <= f[k][j - 1] * a[k + 1][i]) {
						f[i][j] = f[k][j - 1] * a[k + 1][i];
						g[i][j] = k;
					}
				}
			}
		}
		cout << f[o][m] << endl;
		long long ans[10005] = {0};
		int t = 0;
		for (int i = m; i >= 1; i--) {
			ans[++t] = a[g[o][i] + 1][o]; //这里的g[o][i]要+1是因为我们的a数组是包含左右端点的;
			o = g[o][i]; //减小长度,已经输出的对我们来说无用;
		}
		for (int i = t; i >= 1; i--) {
			cout << ans[i] << ' ';
		}
		cout << endl;
	}
	return 0;
}
posted @ 2024-02-17 14:55  Peppa_Even_Pig  阅读(48)  评论(9编辑  收藏  举报