若有5个整数和4个运算符(从加减乘除中指定4个),使用任意多的括号,求最大值。

题目:若有5个整数和4个运算符(从加减乘除中指定4个),使用任意多的括号,求最大值。

举例

2 + 3 - 4 * 4 / 5 = 5 - 16 / 5 = 5 - 3 = 2;
(4 / (3 - 2) + 4) * 5=(4 + 4) * 5 = 40;

题目解析

  求解这道题目只需要穷举所有表达式的结果即可。但如果直接把所有数字和运算符进行排列,不定数目的括号排列将是一个难题。考虑到括号的本质是优先运算,所得的结果再加入下一次的运算中,所以可以采取如下的穷举方法:
  1、将整数部分保存为一维数组A{2,3,4,4,5};
  2、将运算符加减乘除分别映射到自然数1,2,3,4并保存为一维数组B{1,2,3,4};
  3、从数组B中取出一个元素作运算符有4种取法,再从数组A中取出两个元素作优先运算,考虑到两个数的的运算有前后之分,有A(5,2) = 20种取法,计算结果放回数组A中,这之后两数组的长度都会减少1。
   假设分别从数组A中取出元素2和3,从数组B中取出元素2,进行正序运算2 - 3 = -1,则数组A变为{-1,4,4,5},数组B变为{1,3,4},两者的长度都减少了1。
  4、 采取递归的方式重复步骤3直到数组A长度为1,数组B为空,就完成了一次表达式的结果运算。比较所有表达式的结果即可找出最大值。

   数组A长度由5到4的方式有 A ( 5 , 2 ) × 4 = 5 × 4 × 4 A(5,2) \times 4 = 5 \times 4 \times 4 A(5,2)×4=5×4×4种;由4到3的方式有 A ( 4 , 3 ) × 3 = 4 × 3 × 3 A(4,3) \times 3 = 4 \times 3 \times 3 A(4,3)×3=4×3×3种…依次类推,所有的表达式共有 ( 5 × 4 × 4 ) × ( 4 × 3 × 3 ) × ( 3 × 2 × 2 ) × ( 2 × 1 × 1 ) × 1 = 5 × ( 4 ! ) 3 = 69120 (5 \times 4 \times 4) \times (4 \times 3 \times 3)\times (3 \times 2 \times 2) \times (2 \times 1 \times 1) \times 1 = 5 \times(4!)^3 = 69120 (5×4×4)×(4×3×3×(3×2×2)×(2×1×1)×1=5×(4!)3=69120种。同理,若数组A的初始长度为N,则其表达式共有 N × ( ( N − 1 ) ! ) 3 N\times ((N-1)!) ^ 3 N×((N1))3种。当N = 6时,将有10368000种表达式。

算法优化

   由于加法和乘法没有前后之分,则相应的排列A(5,2)可以改为组合C(5,2),数组B中每含有一个加号或乘号,穷举数目都会减半一次。

注意事项

  在除法运算中0不能作分母,且0作分子时结果均为0,所以遇到这种情况时不经计算直接给出结果0。

部分代码说明

  C++中数组长度是固定的,数组的长度不可以缩短但数组的“有效长度”可以,比如数组{2,3,4,4,5}的有效长度为5;数组{-1,0xFFFF,4,4,5}的有效长度为4。在每次递归前都需要对两数组分别进行整理,将无效的0xFFFF后移。

完整的C++代码

#include "stdio.h"

#define N 5
int num[N] = {2,3,4,4,5};
int way[N-1]  = {1,2,3,4};

int methos(int a,int b,int c){//整数a,b和运算符c
    if(c>=1&&c<=4) switch (c){
        case 1:return a+b;break;//加法
        case 2:return a-b;break;//减法
        case 3:return a*b;break;//乘法
        case 4:if(b!=0) return a/b;//除法
                else return -0xFFFF;break;//若b=0不能做分母
        default:return -0xFFFF;break;
	}
}

void zhengli_A(int num[N]){//整理数组A,有效数字左移
    int *p=num;
    for(int i=0;i<N-1;i++) if(p[i]==0xFFFF){
		int Y=0;//有效移动标志
		for(int j=i;j<N-1;j++){
		  p[j]=p[j+1];//左移
		  if(p[j+1]!=0xFFFF) Y=1;//至少有效移动一次
		}
		if(Y==1) i--;//下标左移
		p[N-1]=0xFFFF;//置后
	}
}

void zhengli_B(int way[N-1]){//整理数组B,有效数字左移
    int *p=way;
    for(int i=0;i<N-2;i++) if(p[i]==0xFFFF){
		int Y=0;
		for(int j=i;j<N-2;j++){
		  p[j]=p[j+1];//左移
		  if(p[j+1]!=0xFFFF) Y=1;//至少有效移动一次
		}
		if(Y==1) i--;//下标左移
		p[N-2]=0xFFFF;//置后
	}
}

void buzhou_printf(int buzhou[N-1][4]){//步骤显示输出
    int (*pzb)[4]=buzhou;
    for(int i=0;i<N-1;i++) {//每个表达式都有N-1个步骤
        printf("(%d",pzb[i][0]);//显示第一个数
        switch(pzb[i][1]){//显示运算符
            case 1:printf("+");break;
            case 2:printf("-");break;
            case 3:printf("*");break;
            case 4:printf("/");break;
            default:printf("?");break;
		} printf("%d=%d)",pzb[i][2],pzb[i][3]);//显示第二个数及结果
	} printf("\n");//每个表达式后换行
}

int C_max(int num[N],int way[N-1],int buzhou[N-1][4],int sum_max[2]){//数组A,数组B,步骤,结果
    int *pa=num, *pb=way, (*pzb)[4]=buzhou, *psm=sum_max;
    int i,j,k,m;
	int length=0; for(i=0;i<N;i++) if(pa[length]<0xFFFF) length++;//获取数组A有效长度
    int num_new[N]; for(i=0;i<length;i++) num_new[i]=pa[i];//暂存数组A
	int way_new[N-1]; for(i=0;i<length-1;i++) way_new[i]=pb[i];//暂存数组B
    if(length==1) {//若数组A有效数字只有1个
        psm[0]++;//表达式总数加1
        if(pa[0]>=psm[1]){//若不小于当前最大值
            psm[1]=pa[0];//更新最大值
            buzhou_printf(pzb);//显示步骤
        }
        return pa[0];//返回数组A中唯一有效的数
    }
    else for(k=0;k<length-1;k++) //先从数组B中取运算符以决定采用排列或是组合
           for(i=0;i<length;i++)//从数组A中取出一个数作前数
               if(pb[k]==2||pb[k]==4){//减或除采用排列
				   for(j=0;j<length;j++) if(j!=i) {//从数组A中取出不同下标的第二个数作后数                   
						pzb[N-length][0]=pa[i];pzb[N-length][1]=pb[k];pzb[N-length][2]=pa[j];//保存两个整数和运算符
						pzb[N-length][3]=methos(pa[i],pa[j],pb[k]);//保存结果
						pa[i]=pzb[N-length][3];pa[j]=0xFFFF;pb[k]=0xFFFF;//结果放回数组
						zhengli_A(pa);zhengli_B(pb);//整理数组
						C_max(pa,pb,pzb,psm);//递归
						for(m=0;m<length;m++) pa[m]=num_new[m];//还原数组A
						for(m=0;m<length-1;m++) pb[m]=way_new[m];//还原数组B
				   }
			   }
				else for(j=i+1;j<length;j++){//加或乘采用组合,从数组A中取出第二个数(下标大于第一个数的下标)
                    pzb[N-length][0]=pa[i];pzb[N-length][1]=pb[k];pzb[N-length][2]=pa[j];//保存两个整数和运算符
					pzb[N-length][3]=methos(pa[i],pa[j],pb[k]);//保存结果
                    pa[i]=pzb[N-length][3];pa[j]=0xFFFF;pb[k]=0xFFFF;//结果放回数组
					zhengli_A(pa);zhengli_B(pb);//整理数组
                    C_max(pa,pb,pzb,psm);//递归
                    for(m=0;m<length;m++) pa[m]=num_new[m];//复原数组A 
					for(m=0;m<length-1;m++) pb[m]=way_new[m];//复原数组B
				}
    return 0;
}

int main(){
    int sum_max[2]={0,-0xFFFF},buzhou[N-1][4];//初始表达式总数为0,最大值为-0xFFFF,步骤为(N-1)*4的二维数组
    zhengli_A(num);zhengli_B(way);//整理数组,有效数字左移
    C_max(num,way,buzhou,sum_max);//穷举所有表达式,得出最大值及其步骤
    printf("\n[sum=%d max=%d]",sum_max[0],sum_max[1]);//显示结果
	return 0;
}


运行效果图

N数N-1运算符运行效果图

拓展延伸

  若有指定的N个数和N-1个双目运算符,使用任意多的括号,求最大值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值