一、简介
本程序的思想和算法来自于C语言教材后的实训项目,程序通过用户输入四个整数计算出能够通过加减乘除得到数字24的所有表达式,程序的设计有别于一般通过穷举实现的方式,效率得到提高。算法介绍如下:
如用户输入1,2,3,4四个数字,先将其看成四个集合即{1},{2},{3},{4},整个叫做第一集群,后通过任意两个集合加减乘除{1,2},{1,3},{1,4},{2,3},{2,4},{3,4}六个集合叫做第二集群,而第三集群由第一集群和第二集群产生,而第四集群可由第一集群和第三集群以及由第二集群自身产生,最后比较第四集群所得到的的值是否24,输出结果
二、程序流程如下:
- 程序调用input函数处理用户输入,并同时生成四个相应的集合
- 调用函数calc,通过其中的list_cross函数产生第二、三、四集群
- 调用含税output输出,并同时删除相同的表达式
- 删除所有集群所占的空间,程序结束
三、主要的数据结构以及算法
为了提高计算精度,使用分数表示每一次计算结果
分数的结构 FRACTION
typedef struct{ int num;//分子 int den;//分母 }FRACTION; //注意分数的符号放在分子上
集群链表节点 s_ item
1 typedef char EXPRESS[40]; //存储具体的表达式,如2*3 2 typedef struct s_item{ 3 FRACTION value; //集合的值 如expr为2*3,value.num=6,value.den=1 4 EXPRESS expr; //表达式 5 int flag[4]; //每一个元素代表是否使用相应的数字,如用户输入了1,2,3,4,flag{1,1,0,0},表示 6 //集合含有数字1和2 7 struct s_item* next; //指向下一节点 8 }ITEM,*PITEM;
主要的算法
分数的四则运算:
1.声明
int commonDivisor(int a,int b);//最大公约数 int commonMultiple(int a,int b);//最小公倍数 //公倍数和公约数用于化简和计算分数 FRACTION plus(FRACTION a,FRACTION b);//分数的加法 FRACTION sub(FRACTION a,FRACTION b);//分数的减法 FRACTION multiple(FRACTION a,FRACTION b);//分数乘法 FRACTION division(FRACTION a,FRACTION b);//分数的除法
2.定义
1 //最大公约数 2 int commonDivisor(int a,int b){ 3 int temp=0; 4 while(b!=0){ 5 temp=a%b; 6 a=b; 7 b=temp; 8 } 9 return a; 10 } 11 12 //最小公倍数 13 int commonMultiple(int a,int b){ 14 return a*b/commonDivisor(a,b); 15 } 16 17 18 //分数的加法 19 FRACTION plus(FRACTION a,FRACTION b){ 20 21 if(a.den==b.den){ //分母相同 22 23 a.num=a.num+b.num; 24 }else{ 25 int cm=commonMultiple(a.den,b.den); 26 a.num=a.num*(cm/a.den)+b.num*(cm/b.den); 27 a.den=cm; 28 } 29 30 //简化a,分子分母同除公约数 31 int cm= commonDivisor(abs(a.num),a.den); 32 a.num/=cm; 33 a.den/=cm; 34 35 return a; 36 } 37 38 //分数减法 39 FRACTION sub(FRACTION a,FRACTION b){ 40 41 if(a.den==b.den){ //分母相同 42 43 a.num=a.num-b.num; 44 }else{ 45 int cm=commonMultiple(a.den,b.den); 46 a.num=a.num*(cm/a.den)-b.num*(cm/b.den); 47 a.den=cm; 48 } 49 //简化a,分子分母同除公约数 50 int cm= commonDivisor(abs(a.num),a.den); 51 a.num/=cm; 52 a.den/=cm; 53 54 return a; 55 } 56 57 //分数乘法 58 FRACTION multiple(FRACTION a,FRACTION b){ 59 60 a.num*=b.num; 61 a.den*=b.den; 62 63 int cm= commonDivisor(abs(a.num),a.den); 64 a.num/=cm; 65 a.den/=cm; 66 67 return a; 68 } 69 70 71 //分数的除法 72 FRACTION division(FRACTION a,FRACTION b){ 73 int temp; 74 if(b.num==0){ 75 a.num=0; 76 a.den=0; 77 return a;//不能除0 ,返回分子,分母为0,作为标志 78 }else if(b.num>0){ 79 temp=b.num; 80 b.num=b.den; 81 b.den=temp; 82 }else{ 83 temp =abs(b.num); 84 b.num=b.den; 85 b.den=temp; 86 b.num*=-1; 87 } 88 return multiple(a,b); 89 }
集合之间的加减乘除产生新集合
1.声明
PITEM add(PITEM a,PITEM b); //两个相加 PITEM divide(PITEM a,PITEM b); //两个相除 PITEM mutiply(PITEM a,PITEM b); //两个相乘 PITEM subtract(PITEM a,PITEM b); //两个相减
2.定义
1 PITEM add(PITEM a,PITEM b) //两个相加 2 { 3 4 PITEM x=(struct s_item*)malloc(sizeof(struct s_item)); 5 x->value=plus(a->value,b->value); 6 7 int m; 8 for(m=0;m<4;m++){ 9 x->flag[m]=0; 10 } 11 12 13 int k=0; 14 x->expr[k]=‘(‘; 15 int j; 16 for(j=0;a->expr[j]!=‘\0‘;j++){ 17 x->expr[++k]=a->expr[j]; 18 } 19 x->expr[++k]=‘+‘; 20 for(j=0;b->expr[j]!=‘\0‘;j++){ 21 x->expr[++k]=b->expr[j]; 22 } 23 x->expr[++k]=‘)‘; 24 x->expr[++k]=‘\0‘; 25 26 27 int i=0; 28 for(i=0;i<4;i++){ 29 if(a->flag[i]==1){ 30 x->flag[i]=1; 31 } 32 33 if(b->flag[i]==1){ 34 x->flag[i]=1; 35 } 36 37 38 } 39 40 x->next=NULL; 41 42 return x; 43 } 44 45 46 PITEM divide(PITEM a,PITEM b){ //集合相除 47 PITEM x=(struct s_item*)malloc(sizeof(struct s_item)); 48 x->value=division(a->value,b->value); 49 50 int m; 51 for(m=0;m<4;m++){ 52 x->flag[m]=0; 53 } 54 if(x->value.num==0&&x->value.den==0){ 55 free(x); 56 return NULL; 57 } 58 59 int k=0; 60 x->expr[k]=‘(‘; 61 int j; 62 for(j=0;a->expr[j]!=‘\0‘;j++){ 63 x->expr[++k]=a->expr[j]; 64 } 65 x->expr[++k]=‘/‘; 66 for(j=0;b->expr[j]!=‘\0‘;j++){ 67 x->expr[++k]=b->expr[j]; 68 } 69 x->expr[++k]=‘)‘; 70 x->expr[++k]=‘\0‘; 71 72 int i=0; 73 for(i=0;i<4;i++){ 74 if(a->flag[i]==1){ 75 x->flag[i]=1; 76 } 77 78 if(b->flag[i]==1){ 79 x->flag[i]=1; 80 } 81 82 83 } 84 85 x->next=NULL; 86 return x; 87 } 88 PITEM mutiply(PITEM a,PITEM b)//两个相乘 89 { 90 PITEM x=(struct s_item*)malloc(sizeof(struct s_item)); 91 x->value=multiple(a->value,b->value); 92 int m; 93 for(m=0;m<4;m++){ 94 x->flag[m]=0; 95 } 96 int k=0; 97 x->expr[k]=‘(‘; 98 int j; 99 for(j=0;a->expr[j]!=‘\0‘;j++){ 100 x->expr[++k]=a->expr[j]; 101 } 102 x->expr[++k]=‘*‘; 103 for(j=0;b->expr[j]!=‘\0‘;j++){ 104 x->expr[++k]=b->expr[j]; 105 } 106 x->expr[++k]=‘)‘; 107 x->expr[++k]=‘\0‘; 108 109 int i=0; 110 for(i=0;i<4;i++){ 111 if(a->flag[i]==1){ 112 x->flag[i]=1; 113 } 114 115 if(b->flag[i]==1){ 116 x->flag[i]=1; 117 } 118 119 120 } 121 122 x->next=NULL; 123 return x; 124 } 125 126 127 PITEM subtract(PITEM a,PITEM b){ //相减 128 PITEM x=(struct s_item*)malloc(sizeof(struct s_item)); 129 x->value=sub(a->value,b->value); 130 int m; 131 for(m=0;m<4;m++){ 132 x->flag[m]=0; 133 } 134 int k=0; 135 x->expr[k]=‘(‘; 136 int j; 137 for(j=0;a->expr[j]!=‘\0‘;j++){ 138 x->expr[++k]=a->expr[j]; 139 } 140 x->expr[++k]=‘-‘; 141 for(j=0;b->expr[j]!=‘\0‘;j++){ 142 x->expr[++k]=b->expr[j]; 143 } 144 x->expr[++k]=‘)‘; 145 x->expr[++k]=‘\0‘; 146 147 int i=0; 148 for(i=0;i<4;i++){ 149 if(a->flag[i]==1){ 150 x->flag[i]=1; 151 } 152 153 if(b->flag[i]==1){ 154 x->flag[i]=1; 155 } 156 157 158 } 159 160 x->next=NULL; 161 return x; 162 }
核心代码
产生新集群 list_cross
1 //比较集群之间是否有相同数字 2 int cmp(PITEM left,PITEM right){ 3 int i; 4 for(i=0;i<4;i++){ 5 if(left->flag[i]==1&&right->flag[i]==1){ 6 return 1; 7 } 8 } 9 return 0; 10 } 11 12 //结合两个集群产生下一个集群 13 void list_cross(PITEM left,PITEM right,PITEM result){ 14 15 PITEM p,q; 16 for(p=left->next;p!=NULL;p=p->next){ //循环调用两个集群中所有集合 17 for(q=right->next;q!=NULL;q=q->next) 18 if(cmp(p,q)==0){ //只有两集合不含相同数字才运算 19 PITEM temp=NULL; 20 if((temp=add(p,q))!=NULL){ 21 temp->next=result->next; 22 result->next=temp; 23 } 24 if((temp=subtract(p,q))!=NULL){ 25 temp->next=result->next; 26 result->next=temp; 27 } 28 if((temp=divide(p,q))!=NULL){ 29 temp->next=result->next; 30 result->next=temp; 31 } 32 if((temp=mutiply(p,q))!=NULL){ 33 temp->next=result->next; 34 result->next=temp; 35 } 36 37 } 38 } 39 }
因为用户有可能输入相同的数字,所以要消除相同的表达式:
消除重复表达式
1 PITEM p=p4_head->next; //p指向第四集群的头结点,第四集群即最后四个数字都已经使用的集合 2 3 //消除重复的表达式 4 5 PITEM q,pre; 6 for(;p!=NULL;p=p->next){ 7 for(q=p->next,pre=p;q!=NULL;){ 8 if(strcmp(p->expr,q->expr)==0){ 9 10 pre->next=q->next; 11 PITEM temp=q; //pre为p的前一个节点 12 q=q->next; 13 14 free(temp);//消失重复点; 15 temp=NULL; 16 17 }else{ 18 q=q->next; 19 pre=pre->next; 20 } 21 } 22 }
判断集合的值,输出结果
//输出 p=p4_head->next; while(p!=NULL){ if(p->value.num==24&&p->value.den==1){ puts(p->expr); } p=p->next; }
四、运行