在指導學生入門資訊學競賽的過程中,遇到最多的問題就是排列組合。當代資訊學競賽不鼓勵學生們採用強力的語言特性來解決問題,一般建議首先從演算法本身思考,盡量避免利用排列組合演算法實施窮盡(這樣的方法往往報超時)。
但如何快速的實現排列組合列舉這個問題本身,可以作為知識點來訓練學生的基本功。這裡我分享乙個學生寫的程式,基本框架完全沒變,一些變數命名與**風格我幫忙稍作規整。程式包括排列(pn
,mp n,
m)、組合(cn
,mc n,
m)、迴圈。我們今天分享一下他編寫的快速排列演算法做個例子。
如果需要從10個球中,任意抽出3個球,並給出所有球的排列,大部分學生是這樣做的。
for (int n1=0;n1<10;++n1)
;
vec_buf中,放置當前的列舉狀態(m個籃子);vec_bz裡,是n個可用元素是否被使用;swim是當前處理到的籃子位置。
為了避免反覆申請動態記憶體,採用兩個步驟。第一步,入口方法負責初始化結構體、並生成第一批結果。
/*!
pnm 演算法,n,集合數,即總共有幾個小球;m, 子集,每次摸出幾個小球;vec_output 儲存結果的集合
limit 本次最多樣本數,為0會吐出所有結果(記憶體可能溢位)
本函式返回首次給出的樣本數
*/int pnm(int n, int m, std::vector
short> > & vec_output,
tag_nm_state * state, int limit)
if (finished==true)
return
0; int group = 0;
doif (swim==m)
while (hit == false);
if (hit==true)
}else
finished = true;
} while (finished == false && hit == false);
if (group>=limit && limit>0)
break;
}}while(finished == false);
return group;
}
一旦初始狀態欄舉完畢,後續使用者只需要不斷呼叫下面的方法,獲得後續批次的資料。
/*!
pnm_next 使用帶有記憶效應的 tag_nm_state 記錄窮盡進度很好的避免了重新計算的耗時
注意,state、vec_output 一定是被pnm函式初始化後的變數。且limit要與呼叫pnm時一致。
*/int pnm_next(int n, int m, std::vector
short> > & vec_output,
tag_nm_state * state, int limit)
if (finished==true)
return
0; int group = 0;
doif (swim==m)
while (hit == false);
if (hit==true)
}else
finished = true;
} while (finished == false && hit == false);
if (group>=limit && limit>0)
break;
}}while(finished == false);
return group;
}
int main()
groups = pnm_next(30,5,res,&st,65536);
}return
0;}
輸出:
012
345...98
7654
能夠把回溯法應用到實際解題過程中,說明該同學的基本功還是可以的。在3小時的解題過程中,對stl庫的效能特性掌握的不錯,對演算法流程的精細控制也過關。從演算法中龐雜的if判斷,可以看到還是經歷了比較痛苦的除錯過程。
在這個例子的基礎上,通過加入規則,可以很容易的實現cn
,mc n,
m演算法。
int cnm(int n, int m, std::vector
short> > & vec_output,tag_nm_state * state, int limit)
if (finished==true)
return
0; int group = 0;
doif (hit)
else
while (!hit && finished==false)
while (hit == false);
if (hit==true)
}else
finished = true;}}
if (swim==m && finished==false)
while (hit == false);
if (hit==true)
}else
finished = true;
} while (finished == false && hit == false);
}}while(finished == false);
return group;
}int cnm_next(int n, int m, std::vector
short> > & vec_output,tag_nm_state * state, int limit)
if (finished==true)
return
0; int group = 0;
doif (hit)
else
while (!hit && finished==false)
while (hit == false);
if (hit==true)
}else
finished = true;}}
if (swim==m && finished==false)
while (hit == false);
if (hit==true)
}else
finished = true;
} while (finished == false && hit == false);
}}while(finished == false);
return group;
}
struct tag_for_state
;int fnm(const
unsigned
long
long n[/*m*/], int m, std::vector
long
long> > & vec_output,tag_for_state * state, int limit/* = 0*/)
if (finished==true)
return
0; int group = 0;
dowhile (hit == false);
if (hit==true)
}else
finished = true;
} while (finished == false && hit == false);
if (group>=limit && limit>0)
break;
}}while(finished == false);
return group;
}int fnm_next(const
unsigned
long
long n[/*m*/], int m, std::vector
long
long> > & vec_output,tag_for_state * state, int limit/* = 0*/)
if (finished==true)
return
0; int group = 0;
dowhile (hit == false);
if (hit==true)
}else
finished = true;
} while (finished == false && hit == false);
if (group>=limit && limit>0)
break;
}}while(finished == false);
return group;
}
Python實現全排列的一種演算法
列表arr 1,2,3 輸出其全排列。採取遞迴推導的方法來實現。def perm arr 實現全排列 length len arr if length 1 遞迴出口 return arr result 儲存結果 fixed arr 0 rest arr 1 for arr in perm rest ...
一種基於EEPROM儲存結構的微型資料庫
dbs database 是一種基於eeprom儲存結構的微型資料庫,此資料庫的設計目的是解決資料儲存過程中發生異常而導致系統發生嚴重錯誤。使用乙個結構體陣列資料結構實現資料儲存 資料管理 資料應用。1.構建資料庫 2.初始化兩段位址,將資料迴圈逐個寫入資料庫 3.a段資料自校驗,b段資料校驗 當a...
一種基於有序序列mapjoin的方法
在解決資料傾斜問題時,我們經常會採用一種方式 mapjoin,按照hive的實現,mapjoin是將其中一張表在map的過程中載入到記憶體中,但是如果在join的表中,最小的表的資料量也不小的情況下。我們該怎麼辦呢?其中一種解決的方式是 將兩張表需要實現排序 直接用hadoop解決 如下,兩張表都是...