[dfs入門]數字遊戲
阿新 • • 發佈:2021-08-04
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}\)
由它上一個的數加上它上一個的右邊的數,答案為 \(\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; }