DFS回溯-函數遞歸-xiaoz triangles

題目:小z 的三角形

★實驗任務
三角形的第1 行有n 個由“+”和”-“組成的符號,以後每行符
號比上行少1 個,2 個同號下面是”+“,2 個異號下面是”-“ 。
計算有多少個不同的符號三角形,使其所含”+“ 的個數是”-“ 的
個數的一半。n=7 時的1 個符號三角形如下:

+ + - + - + +
+ - - - - +
- + + + -
- + + -
- + -
- -
+

★數據輸入
第一行爲一個整數N(0<N<=12),表示符合三角形的大小。
★數據輸出
輸出所有滿足題意的圖案和總個數(輸出的圖案按圖案中第一行
的字典序排序。例如:n=2 時,按++,+-,-+,--的順序,因爲第一行
爲++的圖案不符合題意,故樣例只輸出了+-,-+,--)符號之間以空格
隔開。
輸入示例輸出示例

2 + -
--
+
--
-
+
3
Hint
可以枚舉第一行’+’和’-’的情況來補充完整整個三角形

此題難點在於如何對第一行進行遍歷,判斷和實現三角形的輸出相對比較簡單,可以使用異或的方法來做。
這裏使用深度優先搜索的函數遞歸調用來對第一行的每一種情況進行分析進而實現回溯,在每一個節點的位置上,都有+-兩種情況,利用遞歸進行搜索判斷。這裏參考了漢森和昭錫的代碼。
代碼1:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int f[20][20];
int n;
int ans=0;
void done()//done()函數執行判斷和輸出符合要求的三角形
{
    
    int i,j;
    for(i=2;i<=n;i++)
    {
        for(j=1;j<=n-i+1;j++)
        {
            if(f[i-1][j]!=f[i-1][j+1])f[i][j]=2;
            else f[i][j]=1;
        }
    }
    int cnt1=0,cnt2=0;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n-i+1;j++)
        {
            if(f[i][j]==1)cnt1++;
            else cnt2++;
        }
    }
    //cout<<cnt1<<" : "<<cnt2<<endl;
    if(2*cnt1==cnt2)ans++;
    else return ;
    
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n-i+1;j++)
        {
            if(f[i][j]==1)cout<<"+ ";
            else cout<<"- ";
        }
        cout<<endl;
    }
    return ;
        
}
void work(int t)
{
    //cout<<1<<endl;
    if(t>n)
    {
    done();
    return ;
    }//遇到終點執行done()部分
    else
    {
        f[1][t]=1;
        t++;
        work(t);//此時該節點爲'+'
        
    t--;
        
        f[1][t]=2;
        t++;
        work(t);//此時該節點爲'-'
    }//實現DFS算法函數遞歸和判斷的部分
}
int main()
{
    cin>>n;
    work(1);
    cout<<ans;
    
    return 0;
}

代碼1相對較好理解,整串代碼的核心是work()函數:

void work(int t)
{
    //cout<<1<<endl;
    if(t>n)
    {
    done();
    return ;
    }//遇到終點執行done()部分
    else
    {
        f[1][t]=1;
        t++;
        work(t);//此時該節點爲'+'
        
    t--;//回到位置,歸位
        
        f[1][t]=2;
        t++;
        work(t);//此時該節點爲'-'
    }//實現DFS算法函數遞歸和判斷的部分
}

work()函數分成兩個部分:

第一個部分

    if(t>n)
    {
    done();
    return ;
    }//遇到終點執行done()部分

此時無法繼續向下搜索,執行done()部分。

第二個部分

    else
    {
//第一個小節
        f[1][t]=1;
        t++;
        work(t);//此時該節點爲'+'
//第二個小節
    t--;//回到位置,歸位
//第三個小節 
        f[1][t]=2;
        t++;
        work(t);//此時該節點爲'-'
    }//實現DFS算法函數遞歸和判斷的部分

雖說只有寥寥幾行代碼,但確實難以理解。
第二個部分可以分成三個小節:第一個小節代表此時停留的這個節點的狀態是+,然後進行向下遍歷的操作。第三個小節與第一個小節類似,代表此時停留的這個節點的狀態是-。第二個小節t--代表歸位(在第一個小節遍歷它的所有情況結束以後進行了一次多餘的t++操作),從而進行接下來的第三個小節的遍歷操作。

大概的意思是,此時該節點的狀態如果是+,進行DFS搜索接下來(當這個節點狀態是+時)所有的情況,然後返回這個節點,再看這個節點此時的狀態如果是-,進行DFS搜索接下來(當這個節點狀態是-時)所有的情況,搜索結束以後,這個節點不管狀態是+,或是-,它接下來所有的可能性都已經搜索過一遍了。
然後返回這個節點之前的節點,再進行判斷。從最末尾開始,一直到最初的起點。

可以想象成一棵樹,從它的果實返回到它的一個枝節,從它的枝節返回到樹的主幹。再從主幹返回到根。
大家可以通過草稿紙上的模擬進行理解。

代碼2:

#include<stdio.h>
const int MAX_M =15;
const int MAX_N =15;
int ans[MAX_M][MAX_N];
int cnt = 0;

void dfs(int n,int i)
{
    if(i<n+1)
    {
        ans[1][i]=0;
        
        dfs(n,i+1);
    }
    else
    {
        int j,k;
        int sum1=0,sum2=0;
        
        for(j=2;j<n+1;j++)
        {
            for(k=1;k<n+2-j;k++)
            {
                ans[j][k]=ans[j-1][k]^ans[j-1][k+1];//使用異或
            }
        }
        
        for(j=1;j<n+1;j++)
        {
            for(k=1;k<n+2-j;k++)
            {
                if(ans[j][k])
                    sum1++;
                else
                    sum2++;
            }
        }
        
        if(sum1==sum2*2)
        {
            cnt++;
            for(j=1;j<n+1;j++)
            {
                for(k=1;k<n+2-j;k++)
                {
                    if(k==1)
                    {
                        if(ans[j][k])
                          printf("-");
                        else
                          printf("+");
                    }
                    else if(ans[j][k])
                        printf(" -");
                    else
                        printf(" +");
                }
                printf("\n");
            }
        }
        return;
    }

    ans[1][i] = 1;
    dfs(n,i+1);
    return ;
}

int main()
{
    int N;
    
    scanf("%d",&N);
    
    dfs(N,1);
    
    printf("%d\n",cnt);
    
    return 0;
}

代碼2與代碼1的區別在於,它使用了異或來補充三角形。而且把條件判斷和輸出三角形放在了同一個函數中。

與本題類似的題目:hdoj2510

參考資料:zzy19961112

2016/3/17
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章