生活随笔
收集整理的這篇文章主要介紹了
【编程之美】24点游戏
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
轉自:http://blog.csdn.net/tianshuai1111/article/details/7713640
一,概述
? ? ? ? 二十四點是一種益智游戲,它能在游戲中鍛煉人們的心算,它往往要求人們將四個數字進行加減乘除(允許使用括號)求得二十四。然后將四個數字的計算公式表示出來。
二,中綴表達式求解
? ? ? ? ?最直接的方法就是采用窮舉法,游戲中可用的運算符只有四種,四個數字每個只能使用一次。
? ? ? ? ?1)不考慮括號情況
? ? ? ? ? ? ? ? 4個數全排列:4!=24種
? ? ? ? ? ? ? ? 需要三個運算符,且運算符可以重復:4*4*4=64
? ? ? ? ? ? ? ? 總計:1536
? ? ? ? ?2)考慮括號(是個難點)
? ? ? ? ? ? ? ? 自己想的加括號:四個數有五種加括號方式: ?(AB)CD ?、?AB(CD)、?A(BC)D 、?A((BC)D) 、?(AB)(CD)、A(B(CD))
? ? ? ? ? ? ? ? ?錯誤點:這里添加括號的時候,需要把四個數都看成相乘。需要加兩個括號來列舉比較直觀
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??AB(CD) ?= ??(AB)(CD)
? ? ? ? ? ? ? ? ?改正后:((AB)C)D ?、?(AB)(CD)?、 (A(BC))D 、?A((BC)D) 、A(B(CD))
? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? 四個運算數五種不同加括號方式的由來。這是一個經典的Catalan數問題。
? ? ? ? ? ? ? ? 這個經典Catalan數問題在組合數學教材上都能找到。原題目是:n 個數相乘, 不改變它們的位置, 只用括號表示不同的相乘順序,令g(n)表示這種條件下構成不同乘積的方法數,令C(n)表示第n個Catalan數。則有g(n)=C(n-1)。前幾個Catalan數為:C(0)=1,C(1)=1,C(2)=2,C(3)=5,C(4)=14,C(5)=42。所以g(4)=C(3)=5。
? ? ? ? ? ? ? ? 根據Catalan數的計算公式,有g(4)=g(1)g(3)+g(2)g(2)+g(3)g(1)。
? ? ? ? ? ? ? ? Catalan數的計算公式也同時提供了構造答案的方法。對于4個數,中間有3個位置,可以在任何一個位置一分為二,被分開的兩半各自的加括號方案再拼湊起來就得到一種4個數的加括號方案:
一個數時:(A),一種
兩個數:g(2)=g(1)g(1),所以是(A)(B)=(AB),一種
三個數:g(3)=g(1)g(2)+g(2)g(1)=(A)(BC)+(AB)(C),兩種
四個數:g(4)=g(1)g(3)+g(2)g(2)+g(3)g(1)
????????????? ?? =(A)[(B)(CD)+(BC)(D)]+(AB)(CD)+[(A)(BC)+(AB)(C)](D)
????????????? ?? =A(B(CD)) + A((BC)D) + (AB)(CD) + (A(BC))D + ((AB)C)D
? ? ? ? ?共有五種。于是寫代碼枚舉這五種加括號的方式即可。這種方法只是一種能得到正確答案的方法,擴展性和效率都極差。而且生成的表達式中也有冗余括號。
[html]?view plaincopy
#include?<iostream>?? #include?<cmath>?? using?namespace?std;?? ?? const?double?Threshold?=?1E-6;?? const?int?CardsNumber?=?4;?? const?int?ResultValue?=?24;?? double?number[CardsNumber];?? string?result[CardsNumber];?? ?? bool?PointsGame(int?n)?? {?? ?????if(n?==?1)?? ?????{?? ??????????//?由于浮點數運算會有精度誤差,所以用一個很小的數1E-6來做容差值?? ??????????//?本書2.6節中討論了如何將浮點數轉化為分數的問題?? ??????????if(fabs(number[0]?-?ResultValue)?<?Threshold)//結果等于24?? ??????????{?? ???????????????cout?<<?result[0]?<<?endl;//輸出表達式?? ???????????????return?true;??? ??????????}?? ??????????else?? ??????????{?? ???????????????return?false;?? ??????????}?? ?????}?? ?? ?????for(int?i?=?0;?i?<?n;?i++)//第一個數(計算時被兩個數結果替換)?? ?????{?? ??????????for(int?j?=?i?+?1;?j?<?n;?j++)//第二個數(計算時候被最后一個數替換)?? ??????????{?? ???????????????double?a,?b;//存放計算的數?? ???????????????string?expa,?expb;//存放表達式中兩個數?? ?? ???????????????a?=?number[i];?? ???????????????b?=?number[j];?? ???????????????number[j]?=?number[n?-?1];//去除第二個數?? ?? ???????????????expa?=?result[i];?? ???????????????expb?=?result[j];?? ???????????????result[j]?=?result[n?-?1];//表達式去除?? ?? ???????????????result[i]?=?'('?+?expa?+?'+'?+?expb?+?')';?? ???????????????number[i]?=?a?+?b;//去除第一個數?? ???????????????if(PointsGame(n?-?1))?? ????????????????????return?true;?? ?? ???????????????result[i]?=?'('?+?expa?+?'-'?+?expb?+?')';?? ???????????????number[i]?=?a?-?b;?? ???????????????if(PointsGame(n?-?1))?? ????????????????????return?true;?? ?? ???????????????result[i]?=?'('?+?expb?+?'-'?+?expa?+?')';?? ???????????????number[i]?=?b?-?a;?? ???????????????if(PointsGame(n?-?1))?? ????????????????????return?true;?? ?? ???????????????result[i]?=?'('?+?expa?+?'*'?+?expb?+?')';?? ???????????????number[i]?=?a?*?b;?? ???????????????if(PointsGame(n?-?1))?? ????????????????????return?true;?? ?? ???????????????if(b?!=?0)?? ???????????????{?? ????????????????????result[i]?=?'('?+?expa?+?'/'?+?expb?+?')';?? ????????????????????number[i]?=?a?/?b;?? ????????????????????if(PointsGame(n?-?1))?? ?????????????????????????return?true;?? ???????????????}?? ???????????????if(a?!=?0)?? ???????????????{?? ????????????????????result[i]?=?'('?+?expb?+?'/'?+?expa?+?')';?? ????????????????????number[i]?=?b?/?a;?? ????????????????????if(PointsGame(n?-?1))?? ?????????????????????????return?true;?? ???????????????}?? ?? ???????????????number[i]?=?a;//將本次循環的結果消除,繼續測試下一對數?? ???????????????number[j]?=?b;?? ???????????????result[i]?=?expa;?? ???????????????result[j]?=?expb;?? ??????????}?? ?????}?? ?????return?false;?? }?? ?? int?main()?? {?? ????int?x;?? ????for(int?i?=?0;?i?<?CardsNumber;?i++)?? ????{?? ?????????char?buffer[20];?? ?????????cout?<<?"the?"?<<?i?<<?"th?number:";?? ?????????cin?>>?x;?? ?????????number[i]?=?x;?? ?????????itoa(x,?buffer,?10);?? ?????????result[i]?=?buffer;?? ????}?? ????if(PointsGame(CardsNumber))?? ????{?? ?????????cout?<<?"Success."?<<?endl;?? ????}?? ????else?? ????{?? ?????????cout?<<?"Fail."?<<?endl;?? ????}?? }??
三,分支限界法求解
[html]?view plaincopy
#include?<iostream>?? #include?<set>?? #include?<string>?? #include?<cmath>?? using?namespace?std;?? ?? #define?N???4???//?4張牌,可變?? #define?RES?24??//?運算結果為24,可變?? #define?EPS?1e-6?? ?? struct?Elem?? {?? ????Elem(double?r,?string&?i):res(r),info(i){}?? ????Elem(double?r,?char*?i):res(r),info(i){}?? ????double?res;?//?運算出的數據?? ????string?info;?//?運算的過程?? ????bool?operator<(const?Elem&?e)?const?? ????{?? ????????return?res?<?e.res;?//?在set的紅黑樹插入操作中需要用到比較操作?? ????}?? };?? ?? int?A[N];???//?記錄N個數據?? //?用二進制數來表示集合和子集的概念,0110表示集合包含第2,3個數?? set<Elem>?vset[1<<N];???//?包含4個元素的集合共有16個子集0-15?? ?? set<Elem>&?Fork(int?m)?? {?? ????//?memo遞歸?? ????if?(vset[m].size())?? ????{?? ????????return?vset[m];?? ????}?? ????for?(int?i=1;?i<=m/2;?i++)?? ????????if?((i&m)?==?i)?? ????????{?? ????????????set<Elem>&?s1?=?Fork(i);?? ????????????set<Elem>&?s2?=?Fork(m-i);?? ????????????set<Elem>::iterator?cit1;?? ????????????set<Elem>::iterator?cit2;?? ????????????//?得到兩個子集合的笛卡爾積,并對結果集合的元素對進行6種運算?? ????????????for?(cit1=s1.begin();?cit1!=s1.end();?cit1++)?? ????????????????for?(cit2=s2.begin();?cit2!=s2.end();?cit2++)?? ????????????????{?? ????????????????????string?str;?? ????????????????????str?=?"("+cit1->info+"+"+cit2->info+")";?? ????????????????????vset[m].insert(Elem(cit1->res+cit2->res,str));?? ????????????????????str?=?"("+cit1->info+"-"+cit2->info+")";?? ????????????????????vset[m].insert(Elem(cit1->res-cit2->res,str));?? ????????????????????str?=?"("+cit2->info+"-"+cit1->info+")";;?? ????????????????????vset[m].insert(Elem(cit2->res-cit1->res,str));?? ????????????????????str?=?"("+cit1->info+"*"+cit2->info+")";?? ????????????????????vset[m].insert(Elem(cit1->res*cit2->res,str));?? ????????????????????if?(abs(cit2->res)>EPS)??? ????????????????????{?? ????????????????????????str?=?"("+cit1->info+"/"+cit2->info+")";?? ????????????????????????vset[m].insert(Elem(cit1->res/cit2->res,str));?? ????????????????????}?? ????????????????????if?(abs(cit1->res)>EPS)?? ????????????????????{?? ????????????????????????str?=?"("+cit2->info+"/"+cit1->info+")";?? ????????????????????????vset[m].insert(Elem(cit2->res/cit1->res,str));?? ????????????????????}?? ????????????????}?? ????????}?? ????return?vset[m];?? }?? ?? int?main()?? {?? ????int?i;?? ????for?(i=0;?i<N;?i++)?? ????????cin?>>?A[i];?? ????//?遞歸的結束條件?? ????for?(i=0;?i<N;?i++)?? ????{?? ????????char?str[10];?? ????????sprintf(str,"%d",A[i]);?? ????????vset[1<<i].insert(Elem(A[i],str));?? ????}?? ????Fork((1<<N)-1);//開始1111?表示四個數??? ????//?顯示算出24點的運算過程?? ????set<Elem>::iterator?it;?? ????for?(it=vset[(1<<N)-1].begin();??? ???????it!=vset[(1<<N)-1].end();?it++)?? ????????{?? ????????????if?(abs(it->res-RES)?<?EPS)?? ????????????????cout?<<?it->info?<<?endl;?? ????}?? } ?
總結
以上是生活随笔為你收集整理的【编程之美】24点游戏的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。