1. 程式人生 > 其它 >[dfs入門]數字遊戲

[dfs入門]數字遊戲

first:題目

描述

給你一個\(N (1 \le N \le 10)\)的全排列,然後把相鄰的數字加起來,得到一個新的列表,其中的數字減少了一個。它們重複這個過程,直到只剩下一個數字\(M\)

例如:

\[3\ 1\ 2\ 4 \]\[4\ 3\ 6 \]\[7\ 9 \]\[16 \]

現在將這個遊戲倒過來,給出\(N,M\) 希望你找出一個字典序最小的全排列出來

輸入

一行,給出\(N\),\(M\)

輸出

一行,輸出某個\(N\)的全排列

second:分析

這道題目我們可以直接枚舉出全排列,然後一一列舉,看合不合法,第一個找到合法的就是答案。

列舉全排列請看這篇文章

統計答案時我們可以設 \(\large f_{i,j}\)

表示在第\(i\)個階段第\(j\)個數的值,我們很容易得到:

\[\Large f_{i,j}=f_{i-1,j}+f_{i-1,j+1} \]

由它上一個的數加上它上一個的右邊的數,答案為 \(\large f_{n,1}\)

是不是很像楊輝三角?

於是我們得到了一個\(O(n\ !\ n^2)\)的演算法。

third:新知

通過觀察樣例並結合上面的推論,我們可以發現每個數出現的次數就是楊輝三角。

我們設\(5\)個數\(\large a_1,a_2,a_3,a_4,a_5\),最後的答案為:

\[\large a_1* 1+a_2* 4+a_3* 6+a_4* 4+a_5* 1 \]

明顯每個數的次數為楊輝三角第五行。

可以證明:\(i\)個數,第\(j\)個數的次數是楊輝三角的\(\large f_{i,j}\)

所以我們可以預處理每個數出現的次數,在\(O(n)\)統計答案。

時間複雜度為\(O(n\ !\ n+n^2)\)

當然,這道題還可以用狀壓DP來做,有興趣的讀者可以自己思考一下,並完成這道題的加強版。

fourth:注意

1、生成全排列,保證全排列字典序最小

2、當我們找到一組解就輸出並結束程式

3、組合數預處理可以\(O(1)\)查詢

fifth:程式碼

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[21],C[21][21];
bool b[21];
int check()
{
    int sum=0;
    for(int i=1;i<=n;i++)
    sum=sum+a[i]*C[n][i];//統計答案
    return sum;
}
void dfs(int dep)
{
    if(dep==n+1)
    {
        if(check()==m)//符合要求,找到解
        {
            for(int i=1;i<=n;i++)
            printf("%d ",a[i]);//輸出
            exit(0);//結束程式
        }
    return;
    }
    for(int i=1;i<=n;i++)
    {    
        if(b[i]==false)
        {
            a[dep]=i;
            b[i]=true;
            dfs(dep+1);
            b[i]=false;
            a[dep]=0;//生成全排列
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=20;i++)
    {
        C[i][1]=1;
        for(int j=2;j<=i;j++)
        {
            C[i][j]=C[i-1][j]+C[i-1][j-1];//預處理
        }
    }
    dfs(1);
    return 0;
}