1. 程式人生 > 其它 >[dfs入門]同學排隊

[dfs入門]同學排隊

first:題目

描述

N+Q是班長。在校運動會上,N+Q班要進行隊列表演。N+Q要選出\(2* N\)名同學編隊,每人都被編上一個號,每一個從\(1\)\(N\)的自然數都被某\(2\)名同學佩戴,現在要求將他們排成一列,使兩個編號為\(1\)的同學中間恰好夾\(1\)名同學,兩個編號為\(2\)的同學中間恰好夾\(2\)名同學,·······,兩個編號為\(N\)的同學中間恰好夾\(N\)名同學,N+Q希望知道這樣的排法能否實現。

輸入

僅包括一行,即要處理的\(N\)

\(1\le N\le 13\)

輸出

輸出有多少種合法的排列方式

second:分析

直接dfs即可,使用回朔法,設 \(\large f_i\)

代表第\(i\)個同學有沒有佩戴編號,如果為\(true\)代表已經佩戴,反之,為\(false\)為沒有佩戴。

為了搜尋方便,我們可以規定每個人搜尋是搭檔的序號比他大,也就是說假設你是第\(i\)號,你要佩戴\(j\),那麼你的搭檔是第\(i+j+1\)號同學,而不是第\(i-j-1\)號同學。

先判斷是否佩戴,如果沒有佩戴,在一一列舉可佩戴的編號。

判斷時既要保證自己沒有佩戴,還要保證自己的搭檔沒有佩戴,即設你是第\(i\)
號,你要佩戴\(j\),那麼你也要保證\(i+j+1\)號同學沒有佩戴。

列舉時因為我們已經規定了搭檔順序,所以列舉到\(2n-dep-1\)即可,因為後面的\(dep+1\)

已經不能滿足搭檔了。

如果找到一組解滿足要求,則能實現,答案加一,時間複雜度為\(O(n\ !)\)

third:新知

因為這道題資料比較強,我們的dfs會超時,所以我們還需要剪枝。

我們搜尋時一般是先搜小的數,然後再搜大的數,導致後面很難搜尋成功,在一棵子樹中浪費了許多時間。

我們就可以先搜大的數,留下一些小空間,再搜小的數,更容易成功,時間就可以到,在打個表,可以通過此題。

注意一下,這裡的搜尋深度\(dep\)不是從\(1\)\(n\)了,而是從\(n\)\(1\),所以如果搜尋深度為\(0\)的話,就代表搜到了。

fourth:注意

1、一定要保證自己和搭檔都沒有佩戴。

2、倒序搜尋更快。

3、及時回溯。

4、列舉時列舉到\(2 n-dep-1\)

5、\(dep\)\(0\)時代表找到解。

fifth:補充

大家肯定覺得奇怪,為什麼這道題目既然要打表,還要有這麼強的資料,所以肯定還可以優化。

我們用上面的程式跑一跑,就會發現當\(n\%4=1\)\(0\)時無解,即\(ans=0\),於是我們就得到了一個猜想,證明如下:

設問題的一個可行解為\(\large a_1,a_2,······,a_n\),其中$ a_i\(為標號為\)i$的數字的位置,這些數字的對應數字的位置應該為

\(a_1+1+1,a_2+2+1,······,a_n+n+1\)

\(2n\)個整數

\(a_1,a_2,······,a_n,a_1+1+1,a_2+2+1,······,a_n+n+1\)

正是\(1,2,······,2n\),因而

\[\begin{aligned} \sum_{i=1}^na_i+\sum_{i=1}^{n}(a_i+i+1)&=\sum_{i=1}^{2n}i\\ 2(\sum_{i=1}^na_i)+n(n+1)/2+n&=2n(2n+1)/2\\ 2(\sum_{i=1}^na_i)&=(3n^2-n)/2\\ 4(\sum_{i=1}^na_i)&=n(3n-1)\\ \end{aligned} \]

可見\(n(3n-1)\)應該是\(4\)的倍數,當\(n\%4=0,1,2,3\)時,\(n(3n-1)\%4=0,2,2,0\),故\(n\%4=1\)\(2\)時,無解,證畢。

sixth:程式碼

#include<bits/stdc++.h>
using namespace std;
int ans,n;
int a[10001];
void dfs(int dep)
{
    if(dep==0)//找到解
    {
        ans++;
        return;
    }
    for(int i=1;i<=2*n-dep-1;i++)//列舉
    {
        if(a[i]==0&&a[i+dep+1]==0)//自己和搭檔都沒有佩戴
        {
            a[i]=dep;
            a[i+dep+1]=dep;//則佩戴
            dfs(dep-1);
            a[i]=0;//回溯
            a[i+dep+1]=0;
        }
    }
}
int main()
{
    cin>>n;
    if(n%4==1||n%4==2)//特判
    {
        cout<<0;
        return 0;
    }
    dfs(n);
    cout<<ans;
    return 0;
}