【力扣刷题】排列小球(DFS)

力扣链接:https://leetcode-cn.com/leetbook/read/didiglobal2/e7hh2i/

给定三种类型的小球 P、Q、R,每种小球的数量分别为 np、nq、nr 个。现在想将这些小球排成一条直线,但是不允许相同类型的小球相邻,问有多少种排列方法。如果无法组合出合适的结果,则输出 0。

格式:

输入:一行以空格相隔的三个数,分别表示为 np,nq,nr。
输出:排列方法的数量。

示例:

输入:2 1 1
输出:6
解释:如若 np=2,nq=1,nr=1 则共有 6 种排列方式:PQRP,QPRP,PRQP,RPQP,PRPQ 以及 PQPR。

解法:通过dfs递归求解。

#include<bits/stdc++.h>
using namespace std;

int dfs(int last_color, int np, int nq, int nr){
    // if balls of any color run out
    if (np<0||nq<0||nr<0){
        return 0;
    }
    // if only one ball remains 
    if (np+nq+nr==1){
        if (last_color==0) return np;
        if (last_color==1) return nq;
        if (last_color==2) return nr;
    }else{ 
        // back to n-1
        if (last_color==0) return dfs(1,np-1,nq,nr)+dfs(2,np-1,nq,nr);
        if (last_color==1) return dfs(0,np,nq-1,nr)+dfs(2,np,nq-1,nr);
        if (last_color==2) return dfs(0,np,nq,nr-1)+dfs(1,np,nq,nr-1);
    }
    return 0;
}

int main(){
    int np, nq, nr;
    cin>>np>>nq>>nr;
    // 3 colors 
    int ans = dfs(0,np,nq,nr)+dfs(1,np,nq,nr)+dfs(2,np,nq,nr);
    cout<<ans<<endl;
    return 0;
}

很不幸,上面的解法超时了,测试用例通过3/5,那么加个dp数组进行记忆化搜索。

#include<bits/stdc++.h>
using namespace std;
// 用int不够,改为long long
typedef long long LL;

LL p,q,r;

inline LL I(int c,int x,int y,int z){
    return c*(p+1)*(q+1)*(r+1)+x*(q+1)*(r+1)+y*(r+1)+z;
}

vector<LL> dp;

LL find(int c,int i,int j,int k){
    if(i<0||j<0||k<0) return 0;
    LL idx=I(c,i,j,k);
    if(dp[idx]==-1){
        dp[idx]=0;
        if(c==0) dp[idx]+=find(1,i-1,j,k)+find(2,i-1,j,k);
        if(c==1) dp[idx]+=find(0,i,j-1,k)+find(2,i,j-1,k);
        if(c==2) dp[idx]+=find(0,i,j,k-1)+find(1,i,j,k-1);
    }
    return dp[idx];
}

int main(){
    cin>>p>>q>>r;
    dp.resize(3*(p+1)*(q+1)*(r+1));
    fill(dp.begin(),dp.end(),-1);
    dp[I(0,1,0,0)]=1;
    dp[I(1,0,1,0)]=1;
    dp[I(2,0,0,1)]=1;
    cout<<find(0,p,q,r)+find(1,p,q,r)+find(2,p,q,r)<<endl;
    return 0;
}