暑假集训Day1 整数划分

时间:2023-03-08 20:52:38
暑假集训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\n",dp[n][m]);
Path(n,m);
printf("\n");
}
return 0;
}

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