问题描述
题目分析
这是一道典型的动态规划问题,我们假设L(s,t)为I的从s位开始的t位数字组成的十进制数。f(i,j)表示L(0,L)的最大j乘积,则f(i,j)具有最优子结构性质。计算f(i,j)的动态规划递归式如下:
f(i,j) = max{f(k,j-1) * L(k,i-k)} (1 <= k <= i)
这个式子的意思是我们假设在第k位之前的最大j-1乘积是f(k,j-1),所以我们只需要乘上第j个数字,即可 完成最大j乘积,因此从k位开始的i-k个数字组成的整体数字就是我们的第j个数字,二者相乘,求出最大即可。
代码
O#include <iostream>
#include <fstream>
using namespace std;
void solve(int n,int m)
{
int i,j,k;
int temp,maxt,tk;
for(i = 0;i <= n;i++)
{
//f[i][1]就表示一个数字自己做乘积,也就是这个数字本身了。
f[i][1] = conv(0,i);
}
//最大乘积数,即我们要求最大的m乘积问题,所以应该从最大二乘积开始遍历
for(j = 2;j <= m;j++)
{
//要查找的字符串的末尾,由于字符串整个长度为n,所以要查找的末尾最大为n
//又因为如果要分成j个最大乘积,字符串至少有j个字符
//所以将要被查找的字符串的长度至少为j,因此字符串的末尾长度至少为j
for(i = j;i <= n;i++)
{
for(k = 1,temp = 0;k < i;k++)
{
maxt = f[k][j-1] * conv(k,i-k);
if(temp < maxt)
{
temp = maxt;
tk = k;
}
}
}
f[i][j] = temp;
//记录长度为i的字符串达到最大j乘积时的分割位置,即从哪里分割开第j-1个乘即和第j个乘积
ka[i][j] = tk;
}
}
//将字符串中从i开始的j个字符转换为数字
int conv(int i,int j)
{
string str1 = str.substr(i,j);
//c_str以char的形式传回string串,所以某个函数需要char做参数可以使用c_str
return atoi(str1.c_str);
}
//输出函数
void out(int n,int m )
{
ofstream cout("out.txt");
cout<<f[n][m]<<endl;
for(int i =m,k = n;i >= 1 &&k > 0;k = ka[k][i],i--)
{
cout<<"f["<<k<<"]["<<i<<"]="<f[k][i]<<endl;
}
}
int main()
{
int n,m;
ifstream cin1("in.txt");
cin1>n>>m;
if((n < m || (n == 0)))
{
cout<<0<<endl;
return;
}
cin1>>str;
if(n != str.size())
{
cout<<0<<endl;
return;
}
f.resize(n+1,m+1);
ka.resize(n+1,m+1);
solve(n,m);
out(n,m);
return 0;
}
总结
时间复杂度为O(mn^2)