题目链接: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; }
总结:
应该是个基础的动态规划题目吧,代码写得确实有点烂,不过懒得改了,就这样,再接再励吧。