題目鏈接:https://leetcode-cn.com/leetbook/read/didiglobal2/e7hh2i/
題目來源:力扣(LeetCode)
題目描述:
給定三種類型的小球 P、Q、R,每種小球的數量分別為 np、nq、nr 個。現在想將這些小球排成一條直線,但是不允許相同類型的小球相鄰,問有多少種排列方法。如若 np=2,nq=1,nr=1 則共有 6 種排列方式:PQRP,QPRP,PRQP,RPQP,PRPQ 以及 PQPR。如果無法組合出合適的結果,則輸出 0。
輸入描述:
一行以空格相隔的三個數,分別表示為 np,nq,nr。
輸出描述:
排列方法的數量。
輸入樣例:
2 1 1
輸出樣例:
6
解題思路:
按排列中第一個小球的類型不同(P、Q或R),可將排列分為三類。總排列數為三類排列數的總和。設總的合法排列數為fall,第一個球為P的合法排列數為fp(np, nq, nr),第一個小球為Q的合法排列數為fq(np, nq, nr),第一個小球為R的合法排列數為fr(np, nq, nr)。則有:
fall = fp(np, nq, nr) + fq(np, nq, nr) + fr(np, nq, nr)
對於fp(np, nq, nr):
1. 若np <= 0,此時無法取出一個P小球放到排列的第一位,因此第一個球為P的合法排列數為0。
2. 若np == 1且nq == 0, nr == 0,此時取出一個P小球放到排列第一位以后,所有的小球都已用盡且不會有相同的小球相臨。因此合法的排列數為1。
3. 若為其它情況,在取出一個P小球放到排列第一位以后,剩余的小球為:np - 1個P小球,nq個Q小球,nr個R小球。要使排列合法,在對剩余小球進行排列時,需要將Q球或者R球放在第一位並保證后續排列合法,因此此時有:fp(np, nq, nr) = fq(np - 1, nq, nr) + fr(np - 1, nq, nr)。
對於fq(np, nq, nr)與fr(np, nq, nr)可做類似的分析。
至此,可通過將原來的問題一步步分解成規模更小的相同問題進行遞歸求解。通過一個輔助的多維數組記錄遞歸過程中產生的重復狀態來提高運算效率。代碼如下:
#include <iostream> #include <vector> using namespace std; int64_t fp(int np, int nq, int nr, vector<vector<vector<vector<int64_t> > > >& rec); int64_t fq(int np, int nq, int nr, vector<vector<vector<vector<int64_t> > > >& rec); int64_t fr(int np, int nq, int nr, vector<vector<vector<vector<int64_t> > > >& rec); int64_t fp(int np, int nq, int nr, vector<vector<vector<vector<int64_t> > > >& rec) { if (np <= 0) return 0; if (np == 1 && nq == 0 && nr == 0) return 1; if (rec[0][np][nq][nr] == -1) { rec[0][np][nq][nr] = fq(np - 1, nq, nr, rec) + fr(np - 1, nq, nr, rec); } return rec[0][np][nq][nr]; } int64_t fq(int np, int nq, int nr, vector<vector<vector<vector<int64_t> > > >& rec) { if (nq <= 0) return 0; if (np == 0 && nq == 1 && nr == 0) return 1; if (rec[1][np][nq][nr] == -1) { rec[1][np][nq][nr] = fp(np, nq - 1, nr, rec) + fr(np, nq - 1, nr, rec); } return rec[1][np][nq][nr]; } int64_t fr(int np, int nq, int nr, vector<vector<vector<vector<int64_t> > > >& rec) { if (nr <= 0) return 0; if (np == 0 && nq == 0 && nr == 1) return 1; if (rec[2][np][nq][nr] == -1) { rec[2][np][nq][nr] = fp(np, nq, nr - 1, rec) + fq(np, nq, nr - 1, rec); } return rec[2][np][nq][nr]; } int main() { int np, nq, nr; cin >> np >> nq >> nr; vector<vector<vector<vector<int64_t> > > > rec(3, vector<vector<vector<int64_t > > >(np + 1, vector<vector<int64_t> >(nq + 1, vector<int64_t>(nr + 1, -1)))); int64_t res = fp(np, nq, nr, rec) + fq(np, nq, nr, rec) + fr(np, nq, nr, rec); cout << res << endl; //system("pause"); return 0; }
總結:
應該是個基礎的動態規划題目吧,代碼寫得確實有點爛,不過懶得改了,就這樣,再接再勵吧。