动态规划|最大k乘积问题(C语言)

题目:
在这里插入图片描述
【分析】
先通过若干个简单例子来观察规律,摸索思路。例如十进制整数 1234 划分为 3 段可有如下情形:
1 × 2 × 34 = 68
1 × 23 × 4 = 92
12 × 3 × 4 = 144(满足要求的解)

以计算正整数 1234 的最大 3 乘积为例,即 I = 1234,n = 4,k = 3(将 1234 分为 3 段)。由于计算乘积时要使用整数中的“一段”数字,则定义 w(s,t) 表示 I 中从第 s 位到第 t 位组成的数,如 w(2,3)=23;按照动态规划算法处理问题的步骤,定义 m(i,j) 表示整数 I 的前 i 位分成 j 段所得的“最大 j 乘积”,显然所求的问题即为计算 m(n,k)。
根据上述定义,显然有:
(1)当 i < j 时(这种情况下整数 I 的前 i 位无法分成 j 段),定义 m(i,j) = 0;
(2)当 j = 1 时(分为 1 段),有 m(i,j) = m(i,1) = w(1,i);
(3)当 j > 1且 j <= i 时,从 I 中的前 d 位数字(d 从 1 循环到i-1)可得到“最大 j-1 乘积(即 j-1 分段)”,然后乘以剩下的数字(组成 1 段,为 w(d+1,i),即从 d+1 位开始,到 I 的第 i 位),写成状态方程即为:

在这里插入图片描述

计算过程中会建立 2 个二维数组 W 和 M,前者是 n x n 阵,储存w(s,t) 的值(表示 I 中从第 s 位到第 t 位组成的数);后者是 n x k 阵,储存 m(i,j) 的值 。以计算正整数 1234 的最大 3 乘积为例,即 I = 1234,n = 4,k = 3,则有:
在这里插入图片描述
综上,可得m(i,j)的递推关系如下:
在这里插入图片描述
代码:

// 使用的库定义
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// ######################################################################## //
//                                                                          // 
//                              下面是数据定义区                               // 
//                                                                          // 
// ######################################################################## //

#define STR_LEN 100
#define TRUE    1
#define FALSE   0

//


// ######################################################################## //
//                                                                          // 
//                           下面是各个子函数定义                               // 
//                                                                          // 
// ######################################################################## //

int  CalcMaxKProduct( int n, int k, int **D, int **P );  //计算正整数的最大 k 乘积
void PrintProductMatrix( int n, int k, int **D, int **P );//显示计算数据 

//

// ######################################################################## //
//                                                                          // 
//                          下面是各个子函数的实现                             // 
//                                                                          // 
// ######################################################################## //

// 使用动态规划法计算两个字符串之间的编辑距离 ...
//
// 测试数据如下 :
// ( 1 ) 1234 划分为 3 段 : 12 * 3 * 4 = 144
// ( 2 ) 自行设计 2 个测试用例
int CalcMaxKProduct( int n, int k, int **D, int **P )
{
  int i, j, t, maxv, tk;

 
  //当i<j无法进行分段 
  for(i = 1;i <= n;i++)  //分成一段 j=1 
  {
  	P[i][1]= D[1][i];
  }
  

  for(i = 1; i <= n;i++)  //DP过程 
  {
  	for(j = 2;j <= i;j++)   //当 j > 1且 j <= i 时,从 I 中的前 t 位数字(t 从 1 循环到i-1)可得到“最大 j-1 乘积(即 j-1 分段) 
	{
		maxv = 0;
		for(t = 1;t < i;t++)
		{
			if((tk = P[t][j-1]*D[t+1][i]) > maxv)  //然后乘以剩下的数字(组成 1 段,为 D(t+1,i),即从 t+1 位开始,到 I 的第 i 位), 
				maxv = tk;
		
		}
		P[i][j] = maxv;
    }
  }
  return P[n][k];
}

// 打印编辑距离矩阵 ...
void PrintProductMatrix( int n, int k, int **D, int **P )
{
  int i, j;

  // 打印 'D' ...
  printf( "\t生成的整数分段值矩阵为 : \n" );

 for(i=1 ; i<= n; i++)
 {
   for(j=1 ; j<= n; j++)
   {
   	 printf("%d ",D[i][j]);
     printf(" ");
   } 
   printf("\n");
   
 }
   


  printf( "\n\n" );

  // 打印 'P' ...
  printf( "\t生成的分段乘积矩阵为 : \n" );

  for(i=1 ; i<= n; i++)
 {
   for(j=1 ; j<= k; j++)
   {
   	printf("%d ",P[i][j]);
    printf(" ");
  }
  printf("\n");
}
  
  printf( "\n\n" );
}

//

// ######################################################################## //
//                                                                          // 
//                             下面是主程序的实现                              // 
//                                                                          // 
// ######################################################################## //

int main()
{
  char StrI[ STR_LEN ];
  char StrK[ STR_LEN ];
  int  *I, n;    // 储存输入的整数及其长度 ...
  int  k;        // 输入的整数分为 'k' 段 ...
  int  **D, **P; // 二维数组, 储存整数的各个分段值及乘积值 ...
  int  i, j, MaxProduct;
  int  IsStop;

  IsStop = FALSE;
  while ( !IsStop )
  {
    // 清屏 ...
    system( "cls" );

	// 输入整数 'I' ...
    printf( "\n\n\t请输入 < 整数 I 的值 > , 输入 < q / Q > 表示结束 : " );
    scanf( "%s", StrI );
	n = strlen( StrI ); // 输入整数的位数 ...
    if ( n > 0 )
	  IsStop = ( ( StrI[ 0 ] == 'q' ) || ( StrI[ 0 ] == 'Q' ) );
    else
      printf( "\t输入的整数不能为空 !\n\n" );

	if ( !IsStop )
	{
	  // 输入分段数 'k' ...
      printf( "\n\t请输入 < 整数分段数 k > , 输入 < q / Q > 表示结束 : " );
      scanf( "%s", StrK );
      if ( strlen( StrK ) > 0 )
	    IsStop = ( ( StrK[ 0 ] == 'q' ) || ( StrK[ 0 ] == 'Q' ) );
      else
        printf( "\t输入的整数分段数不能为空 !\n\n" );

	  // 输入的整数及其分段数 'I' 和 'k' 均不为空, 则计算最大 'k' 乘积 ...
	  if ( !IsStop )
	  {
		// 将输入的字符串转换为整数 ...

		// 输入的整数 'I' ...

        // 动态申请一维数组 ...
        I = ( int * )malloc( ( n + 1 ) * sizeof( int ) );

		// 将输入的字符串整数的每一位放入 'I' 中 ...
		for ( i = 1; i <= n; i++ )
		  I[ i ] = StrI[ i - 1 ] - '0'; // 注意序号起始值 ...

		// 输入的分段数 'k' ...
		k = atoi( StrK ); //把StrK(字符串)转换为一个整数 

        // 动态申请二维数组 'D' 和 'P' ...

		// 'D' - 储存整数的各个分段值( n x n 阵 ) ...
		//定义 D(s,t) 表示 I 中从第 s 位到第 t 位组成的数 
        D = ( int ** )malloc( ( n + 1 ) * sizeof( int ) );
        for ( i = 0; i <= n; i++ )
          D[ i ] = ( int * )malloc( ( n + 1 ) * sizeof( int ) );
		// 初始化 ...
        for ( i = 0; i <= n; i++ )
      	  for ( j = 0; j <= n; j++ )
            D[ i ][ j ] = 0;

		// 'P' - 储存整数的分段乘积值( n x k 阵 ) ...
		//定义 P(i,j) 表示整数 I 的前 i 位分成 j 段所得的“最大 j 乘积” 
        P = ( int ** )malloc( ( n + 1 ) * sizeof( int ) );
        for ( i = 0; i <= n; i++ )
          P[ i ] = ( int * )malloc( ( k + 1 ) * sizeof( int ) );
		// 初始化 ...
        for ( i = 0; i <= n; i++ )
      	  for ( j = 0; j <= k; j++ )
            P[ i ][ j ] = 0;

		// 生成 'D' 矩阵 ...
		//表示 I 中从第 s 位到第 t 位组成的数 
		 for( i = 1 ;i <= n; i++ )
		 {
		  	D[i][i] = I[i];
		  	for(j = i+1;j <= n;j++)
		  	{
		  		D[i][j] = D[i][j-1]*10+I[j];	
			}
		  }
		  

   
        // 计算最大 'k' 乘积 ...
        MaxProduct = CalcMaxKProduct( n, k, D, P );

        // 显示计算结果 ...
        printf( "\n\t< 计算得到的最大 k 乘积为:%d > \n\n", MaxProduct );

		// 打印数值矩阵 ...
		PrintProductMatrix( n, k, D, P );

        // 释放二维数组 'D' 和 'P' 的空间 ...
        for ( i = 0; i <= n; i++ )
          free( D[ i ] );
        free( D );

        for ( i = 0; i <= n; i++ )
          free( P[ i ] );
        free( P );

        // 等待用户输入任意一键返回 ...
        printf( "\n\n" );
        system( "PAUSE" );
	  }
	}
  }

  // 等待用户输入任意一键返回 ...
  printf( "\n\n" );
 return 0;
}

//

运行结果:
在这里插入图片描述

  • 5
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值