一、簡介
本程序的思想和算法來自於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; }
四、運行
源代碼地址:C語言實現24點src