1. 程式人生 > >noi1789 算24 解題報告

noi1789 算24 解題報告

分析:

半年前看到過這道題,當時是用了一段很暴力的程式碼騙過去的;今年學習資料結構時在深搜板塊中發現這道題,這才醒悟它原來是個搜尋題——套用了深搜的模板,帶著許多投機取巧的要素和笨辦法,總算是寫好了。

這道題作為搜尋題有它的獨特之處——先不說減法和除法的方向性,光是計算結果又要插回去繼續參與接下來的搜尋這一點就令人心煩。為了解決這個問題,我設定了一個數組x[4],用於儲存待處理的a[]中的數字:一開始時將四個數字讀到a[]中,x[4]初始化為{0,1,2,3},然後深搜傳遞的引數i是針對x[]的,表示a[]中以x[0]~x[i]之間儲存的內容為下標的數字是待使用的(包括尚未使用的初始資料和尚未使用的計算結果)。然後深搜的目標條件就是i==0,此時a[x[0]]中儲存的資料就是根據之前的搜尋路線得到的結果,把它和24比較,相

等就退出搜尋;不相等就回溯,繼續搜尋。

在搜尋的過程中,我使用了j和k兩個指標指向x[0]~x[i]中的任意兩個不同的數字,分別將它們進行加減乘除四種運算(這裡我偷了個懶,因為j和k兩個指標是任意指的,所以就不用考慮減法和除法的方向性了。)運算後將結果存在a[x[j]]中(在這之前先儲存下a[x[j]]的值,方面後面回溯)。這樣,a[x[j]]中存放著尚未使用的資料,a[x[k]]中存放著已使用過的資料,a[x[i]]中的資料尚未使用(k==i時也不影響程序),而k≤i,所以我交換了x[k]和x[i]的值,保證a[]中以x[0]到x[i-1]中存放的值為下標的資料都是尚待使用的。然後將i-1作為引數傳入下一層搜尋。搜尋

回來後再交換回x[i]和x[k],將一開始存放的a[x[j]]的值再放回去,將一切恢復原狀,進行下一輪搜尋。

具體程式碼如下:

#include<stdio.h>
using namespace std;

double a[4]={0};//用於儲存計算過程中的數字 
int flag;//能否湊出24的標記 
int x[4]={0,1,2,3}; //x[i]中儲存的數為a[]中待使用的數
                    //使用時為for(int i=0;i<dfs的傳遞引數;i++)a[x[i]];
double abs(double a); 
void dfs(int i);;//表示以x[0]~x[i]中的數字為下標的a[]的數字尚未用過

int main(){
    while(true){
        flag=0;
        for(int i=0;i<4;i++)
            scanf("%lf",&a[i]);    
        if(!a[0] && !a[1] && !a[2] && !a[3]) return 0;    
        dfs(3);//表示以x[0]~x[3]中的數字為下標的a[](也就是全部)的數字尚未用過 
        printf("%s\n",flag?"YES":"NO");
    }
}
void dfs(int i){
    if(i==0){//表示此時只有a[x[0]]中的數字尚未用過——這是計算的最後結果 
        if(abs(a[x[0]]-24)<0.0001)
            flag=1;
        return;
    }
    for(int j=0;j<=i;j++)
        for(int k=0;k<=i;k++){
            if(j==k)continue;    //j和k是兩個指標,指向a[]中兩個不同的數字,用於計算 
             for(int l=1;l<=4;l++){//一共加減乘除四種情況,本來減和除有方向的區別,但因為j和k的兩個指標是取了所有情況的,所以就不用了 
                 double tmp=a[x[j]];//a[x[j]]和a[x[k]]的運算結果將儲存在a[x[j]]中進行之後的搜尋,為了回溯,這裡先儲存一下a[x[j]]的值 
                 switch(l){
                     case 1:a[x[j]]=a[x[j]]+a[x[k]];        break;
                    case 2:a[x[j]]=a[x[j]]-a[x[k]];        break;
                    case 3:a[x[j]]=a[x[j]]*a[x[k]];        break;
                    case 4:a[x[j]]=a[x[j]]/a[x[k]];        break;
                //    case 5:a[x[j]]=a[x[k]]-a[x[j]];        break;
                //    case 6:a[x[j]]=a[x[k]]-a[x[j]];        break;
                 }
                {int tmpn=x[k];
                     x[k]=x[i];
                     x[i]=tmpn;}//a[x[k]]在上面已經用過,不再需要,而a[x[i]]尚未用到(i==k時同樣),故交換x[k]和x[i],保證在x[i]以前的所有x[]對應的a[]都是尚待使用的 
                dfs(i-1);
                {int tmpn=x[k];
                     x[k]=x[i];
                     x[i]=tmpn;}//交換回來,恢復原狀 
                a[x[j]]=tmp;//恢復原狀,開始下一輪搜尋 
                if(flag)
                    return;
             }
        }
    return;
}
double abs(double a){
    if(a>0)return a;
    return -a;