在咱們作題中,搜索也好,動態規劃也好,咱們每每有時候須要用一個數字表示一種狀態php
好比有8個燈泡排成一排,若是你用0和1表示燈泡的發光狀況ios
那麼一排燈泡就能夠轉換爲一個二進制數字了c++
好比算法
01100110 = 102數組
11110000 = 240ide
10101010 = 170測試
經過這些十進制數,只要把他們展開,咱們就知道燈泡的狀態了spa
若是這題是一個動態規劃題code
而後咱們就拿這些數字作一些轉移了,blog
好比dp[102],dp[240],dp[170]等等
這對題目頗有幫助
上面講的那些就是所謂的狀態壓縮了,須知詳細的狀態壓縮能夠去百度
或者有機會我本身去寫一篇博客(這是flag(/TДT)/)
那對於有些題,咱們即便狀態壓縮後,數字太大,數組都開不下,麻煩的題目(/TДT)/
這些題目也要看狀況,好比我接下來要講的康託展開
康託展開經典題:hdu 1430
http://acm.hdu.edu.cn/showproblem.php?pid=1430
在魔方風靡全球以後不久,Rubik先生髮明瞭它的簡化版——魔板。魔板由8個一樣大小的方塊組成,每一個方塊顏色均不相同,可用數字1-8分別表示。任一時刻魔板的狀態可用方塊的顏色序列表示:從魔板的左上角開始,按順時針方向依次寫下各方塊的顏色代號,所獲得的數字序列便可表示此時魔板的狀態。例如,序列(1,2,3,4,5,6,7,8)表示魔板狀態爲:
1 2 3 4
8 7 6 5
對於魔板,可施加三種不一樣的操做,具體操做方法以下:
A: 上下兩行互換,如上圖可變換爲狀態87654321
B: 每行同時循環右移一格,如上圖可變換爲41236785
C: 中間4個方塊順時針旋轉一格,如上圖可變換爲17245368
給你魔板的初始狀態與目標狀態,請給出由初態到目態變換數最少的變換步驟,如有多種變換方案則取字典序最小的那種。
咱們看這題,總共有8個數字,1~8,假如咱們把他們當作0~7
那麼每一個數字能夠轉換爲一個3位二進制
0:000
1:001
2:010
3:011
4:100
5:101
6:110
7:111
而後12345678這個狀態咱們能夠表示爲二進制000001010011100101110111,總共3*8=24位,
2^24 = 16777216,數組根本開不下啊
這時,咱們發現了,有一些狀態,根本沒有用到,由於這題已經規定了有8個數字,每一個數字只出現一次
好比000000000000000000000000這個狀態,你說可能出現嗎?(o ° ω ° O )
這個時候,康託就對這種題目作了研究(o ° ω ° O )
這種每一個數字只出現一次的問題的因此狀況,總共才n!個狀況(這個問題叫作全排列)
康託的一套算法能夠正好產生n!個數字
好比:
123 -> 0
132 -> 1
213 -> 2
231 -> 3
312 -> 4
321 -> 5
這是如何作到的(/≥▽≤/)
在峯神的博客裏面有很好的解釋(對不起了峯神≖‿≖✧,拿過來抄一下)
(/≥▽≤/)好神奇
因而乎,康託展開模板:
1 void cantor(int s[], LL num, int k){//康託展開,把一個數字num展開成一個數組s,k是數組長度 2 int t; 3 bool h[k];//0到k-1,表示是否出現過 4 memset(h, 0, sizeof(h)); 5 for(int i = 0; i < k; i ++){ 6 t = num / fac[k-i-1]; 7 num = num % fac[k-i-1]; 8 for(int j = 0, pos = 0; ; j ++, pos ++){ 9 if(h[pos]) j --; 10 if(j == t){ 11 h[pos] = true; 12 s[i] = pos + 1; 13 break; 14 } 15 } 16 } 17 } 18 void inv_cantor(int s[], LL &num, int k){//康託逆展開,把一個數組s換算成一個數字num 19 int cnt; 20 num = 0; 21 for(int i = 0; i < k; i ++){ 22 cnt = 0; 23 for(int j = i + 1; j < k; j ++){ 24 if(s[i] > s[j]) cnt ++;//判斷幾個數小於它 25 } 26 num += fac[k-i-1] * cnt; 27 } 28 }
付上AC代碼:
(這代碼我在杭電上用c++交居然CE了,g++就沒問題,CE的內容是個人那個模板,說什麼不能bool h[k]這樣聲明類型,c++當心眼,這有什麼關係嘛(´・ω・)ノ,我還只是個孩子)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<string> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 typedef long long LL; 9 const int N = 8; 10 queue <LL> que; 11 string ans[50000]; 12 char str1[10], str2[10]; 13 bool vis[50000]; 14 15 int map[10];//映射 16 int num[10]; 17 18 LL fac[N];//階乘 19 void change(int s[], int o){//o分別是0,1,2,表示ABC三種變化 20 switch(o){ 21 case 0: 22 for(int i = 0; i < 4; i ++) swap(s[i], s[8-i-1]); 23 break; 24 case 1: 25 for(int i = 3; i >= 1; i --) swap(s[i], s[i-1]); 26 for(int i = 4; i < 7; i ++) swap(s[i], s[i+1]); 27 break; 28 case 2: 29 swap(s[1], s[6]); 30 swap(s[6], s[5]); 31 swap(s[5], s[2]); 32 break; 33 } 34 } 35 void cantor(int s[], LL num, int k){//康託展開,把一個數字num展開成一個數組s,k是數組長度 36 int t; 37 bool h[k];//0到k-1,表示是否出現過 38 memset(h, 0, sizeof(h)); 39 for(int i = 0; i < k; i ++){ 40 t = num / fac[k-i-1]; 41 num = num % fac[k-i-1]; 42 for(int j = 0, pos = 0; ; j ++, pos ++){ 43 if(h[pos]) j --; 44 if(j == t){ 45 h[pos] = true; 46 s[i] = pos + 1; 47 break; 48 } 49 } 50 } 51 } 52 void inv_cantor(int s[], LL &num, int k){//康託逆展開,把一個數組s換算成一個數字num 53 int cnt; 54 num = 0; 55 for(int i = 0; i < k; i ++){ 56 cnt = 0; 57 for(int j = i + 1; j < k; j ++){ 58 if(s[i] > s[j]) cnt ++;//判斷幾個數小於它 59 } 60 num += fac[k-i-1] * cnt; 61 } 62 } 63 void init(){ 64 fac[0] = 1; 65 for(int i = 1; i < N; i ++) fac[i] = fac[i-1] * i; 66 int a[8], b[8]; 67 LL temp, temp2; 68 que.push(0); 69 vis[0] = true; 70 while(!que.empty()){ 71 LL temp = que.front(); que.pop(); 72 cantor(a, temp, 8); 73 for(int i = 0; i < 3; i ++){ 74 copy(a, a+8, b); 75 change(b, i); 76 inv_cantor(b, temp2, 8); 77 if(!vis[temp2]){ 78 que.push(temp2); 79 vis[temp2] = true; 80 ans[temp2] = ans[temp] + (char)('A' + i); 81 } 82 } 83 } 84 } 85 int main(){ 86 init(); 87 while(~scanf("%s", str1)){ 88 scanf("%s", str2); 89 //先把全部初始狀態都轉換成12345678 90 //最終狀態根據初始狀態的轉換而轉換 91 //這樣只要一次預處理就能夠解決問題了 92 for(int i = 0; i < 8; i ++) map[str1[i] - '0'] = i + 1; 93 for(int i = 0; i < 8; i ++) num[i] = map[str2[i] - '0']; 94 LL temp; 95 inv_cantor(num, temp, 8); 96 cout << ans[temp] << endl; 97 } 98 }
宇宙我來啦~\(≧▽≦)/~