暑假集训Day1 整数划分

题目大意:

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

输入格式:

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

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

输出格式

对于每组数据

第一行输出最大值。

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

算法分析:

第一问求dp值就是简单的dp 具体实现可参见 暑假集训day1 水题 乘法最大

1.做第一问的时候注意这个题给出的M并不是乘号数量而是分成的份数,所以读入M之后记得M-1,到后面便历乘号数量j的时候也是min(i-1,m)。
2.记录路径的方法也跟平时记录路径一样,如果更改了dp的值说明在这个位置插入了乘号,就让path[i][j] = k(k记录断点,i记录前i个数字,j表示共有j个乘号)
3.最后递归输出就可以了,递归函数传参x为当前为前x个数字,t表示还有t个乘号没有插入
(注意一个细节,递归边界是t=-1而不是t=0,因为t等于0的时候表示的是有0个乘号但是仍然是分成一份,即仍然有值,只有当t遍历到-1的时候才说明没有东西可以递归了)

代码展示

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int a[maxn],n,T,path[maxn][maxn],m;
long long dp[maxn][maxn],sum[maxn][maxn];
char s[maxn];

void clear(){
	n = strlen(s+1);
	memset(sum,0,sizeof(sum));
	memset(dp,0,sizeof(dp));
	for(int i = 1;i <= n;++i)
			for(int j = i;j <= n;++j)
				sum[i][j] = sum[i][j-1]*10 + s[j] - '0';
	for(int i = 1;i <= n;++i)dp[i][0] = sum[1][i];
	return ;
}

void Path(int x,int t){
	if(t == -1)return;
	Path(path[x][t],t-1);
	printf("%lld ",sum[path[x][t]+1][x]);
}

int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%s %d",s+1,&m);m--;
		clear();
		for(int i = 1;i <= n;++i){
			for(int j = 1;j <= min(i-1,m);++j)
				for(int k = 1;k < i;++k){
					if(dp[i][j] < dp[k][j-1]*sum[k+1][i]){
						dp[i][j] = dp[k][j-1]*sum[k+1][i];
						path[i][j] = k;
					}
				}
		}
		printf("%lld
",dp[n][m]);
		Path(n,m);
		printf("
");
	}
	return 0;
}

制作不易,关注走起>)<

原文地址:https://www.cnblogs.com/2004-08-20/p/13184497.html