思路2 :狀態壓縮
排列型列舉
組合型列舉
例題:這是博主準備藍橋杯的時候的學習筆記,今天是準備的第一天,學習列舉演算法。本文是博主在學習的時候做的一些筆記整合。
題目鏈結: 遞迴實現指數型列舉
指數型列舉,實際就是求出集合 a = [0,1,2,…,n]的所有真子集的組合和排列。求出所有真子集可能全排列的方法為求出長度為0~n的所有排列,求出組合的辦法就是求出所有的固定順序的排列這樣一定能保證結果為組合
例如我要求長度為3的集合a的所有排列,我們只需要將乙個長度為1的空集合b1填滿,把集合a的數選擇進去。再準備乙個長度為2的b2,填b2。以此類推填b3。最後的所有b即為所求;而我要求出長度為3的集合a的所有組合,只需要在原來排列的基礎上在選數的時候規定乙個順序。
排列集合b的第1個位置選了2之後,第2個位置可以填1(非公升序),也可以填3(公升序)。即 2、1、3 與 1、2、3是不同的兩個結果。
**:
#include
#include
using
namespace std;
const
int n =15;
//資料長度
int b[n +1]
;//待求集合b
bool state[n]
;//狀態陣列,用以記錄該數是否被選擇
//經典的dfs
//引數: cur : 當前選到的位置 tar : 待求b集合的長度 ,n : a集合的長度
void
solution
(int cur,
int tar,
int n)
puts(""
);//列印乙個換行
return
;//退出遞迴
}//選數填坑 , 範圍為1 ~ n(可以理解為a集合裡的資料,
//這題比較特殊,a集合代表的是1~n的所有資料)
for(
int i =
1;i <= n; i++)}
}int
main()
組合且公升序
和上面不同的是,上面是第1個位置選了2之後,第2個位置還可以從2之前的數開始填坑,現在是第1個位置選了2之後,第2個位置只能從大於2的數里選了。
即當cur處選擇了num之後,則下次的cur只能從num開始選擇,而不能選擇之前的數。這樣就可以嚴格控制一種情況且保持公升序。
總結:dfs需要四個變數記錄當前狀態:
當前位於的坑cur,當前可以選的最小數字start,當前的目標總坑數n,當前已經填的坑陣列b。
**如下:
#include
#include
using
namespace std;
const
int n =15;
//資料長度
int b[n +1]
;//待求集合b
bool state[n]
;//狀態陣列,用以記錄該數是否被選擇
//經典的dfs
//引數: cur : 當前選到的位置 start : 選到的數,用以保證公升序
// tar : 待求b集合的長度 ,n : a集合的長度
void
solution
(int cur,
int start,
int tar,
int n)
puts(""
);//列印乙個換行
return
;//退出遞迴
}//選數填坑 , 範圍為start ~ n
for(
int i = start ;i <= n; i++)}
}int
main()
列舉集合a中的每乙個元素,每乙個元素都有選擇或不選兩個寫入集合b的方式。使用乙個二進位制數state 來表示該元素是否選擇。根據計算可以得出一共有2^n種組合
例如n = 3:第一位表示1,第二位表示2…第n位表示n,其值為1代表選擇,0代表不選,列舉每種方案即為所求。
000 -> (空)
001 -> 1
010 -> 2
100 -> 3
011 -> 1 2
101 -> 1 3
110 -> 2 3
111 -> 1 2 3
使用迴圈
#include
using
namespace std;
intmain()
cout << endl;
}return0;
}
狀態壓縮遞迴(參考yxc大佬的**)#include
using
namespace std;
int n;
// u是當前列舉到的數,state是二進位制數記錄哪些數被選
void
dfs(
int u,
int state)
dfs (u +
1, state)
;// 不用u這個數
dfs (u +
1, state |(1
<< u));
// 用u這個數
}int
main()
解釋yxc大佬的**
state 是乙個n位二進位制數,第1位表示1,第二位表示2,…,第n位表示n。乙個state 可以表示2^n個數。
state >> i & 1 表示取出二進位制數state的第i位。
state | (1 << u)表示將state的第n位轉為1,而不改變其他位的值,例如state = 10012 ,u = 3 時的運算結果為10112
94. 遞迴實現排列型列舉
利用我們求解指數型列舉的思想,其實求長度為n的集合a的全排列,其實就是求a的 長度為n的所有排列。
例如求解 n = 3 的全排列,其實就是求解 集合a = [1,2,3]的全部長度為3的真子集 b 有 n!個
=> 1 2 3
=> 1 3 2
=> 2 1 3
=> 2 3 1
=> 3 1 2
=> 3 2 1
有了這個理論。我們很快就能根據上面求解指數型列舉的方法求出排列型列舉,由於我們這裡只需要知道集合a的長度n即可,因為我們只需要求出n的排列結果,所以我們不需要引數tar。
#include
#include
using
namespace std;
const
int n =20;
bool vis[n]
;int b[n]
;void
dfs(
int cur,
int n)
cout << endl;
return;}
for(
int i =
1;i <=n ;i ++)}
}int
main()
組合型列舉其實就是指數型列舉的公升序版乙個變種,我們只需要求出公升序指數型列舉的某乙個確定長度的n的結果b其實就是結果。例如我們要求c(m,n)就是求乙個長度為n的集合a的長度為m的所有真子集。
#include
#include
using
namespace std;
const
int n =20;
bool vis[n]
;int b[n]
;void
dfs(
int cur,
int start,
int n,
int m)
cout << endl;
}for
(int i = start;i <=n ;i ++)}
}int
main()
100 可以表示為帶分數的形式:100=3+69258/714
還可以表示為:100=82+3546/197
注意特徵:帶分數中,數字 1∼9 分別出現且只出現一次(不包含 0)。
類似這樣的帶分數,100 有 11 種表示法。
輸入格式
乙個正整數。
輸出格式
輸出輸入數字用數碼 1∼9 不重複不遺漏地組成帶分數表示的全部種數。
資料範圍
1≤n<106
輸入樣例1:
100輸出樣例1:
11輸入樣例2:
105輸出樣例2:
6利用前面求解全排列的思路求解出數字1~9的全排列
將全排列結果劃分成三個部分,每乙個部分表示乙個數 例如將 123456789 劃分為 1234 567 89 三個數.
列舉這三個部分,檢驗是否符合題目條件
#include
#include
using
namespace std;
const
int n =10;
int target;
//題目輸入的正整數
int num[n]
;//用以儲存全排列的結果
bool vis[n]
;//dfs記錄陣列 : vis[i]表示數字i是否被訪問過
int cnt;
// 記錄答案
//用以將dfs函式中劃分的部分排列組成乙個整數
intcalc
(int l,
int r)
intdfs
(int cur)
return0;
}//搜尋模板
for(
int i =
1;i <=
9;i ++)}
}int
main()
藍橋杯備賽筆記 規律題
今年大三,開始準備藍橋杯大賽,在做了第七屆以及第六屆的題以後,對於藍橋杯的考點以及考查方式有了一定的了解,這裡做乙個小結。藍橋杯的考察點每年都會考三類題型,第一種是規律題,典型特徵是給出乙個變化情況,讓你來找出其中的規律,並且根據這個規律開回答它的問題。第二種是考察對於暴力求解的方法的理解與使用情況...
藍橋杯備賽筆記 暴力解法
今天是做比賽總結的第二天,今天總結的是用暴力解法解決題目。這種方法常常用在解決例如湊算式等問題的求解上,思路比較簡單,所以考點一般設定在對於細節的處理上面,換句話講,如果細節處理不當,很容易造成丟分。具體來說,有這麼一道題,就是典型的暴力解法 第七屆藍橋杯第三題 的應用 湊算式b def a 10 ...
藍橋杯入門訓練 藍橋杯備賽
begin 1 a b問題str1 input str2 str1.split s int str2 0 int str2 1 print s begin 2 序列求和s int input 用公式計算而不用迴圈,避免超時 n s s 1 2print int n begin 3 圓的面積impor...