题目传送门
题意:
有n个人,身高分别是1,2,3,4…n,将这n个人排队,要求从前(上面)往后(下面)身高增,从左往右身高也递增。要求组成k排,每排需要的人数给出,问你有多少种排列的方法。
比如n=6,k=3,N1=3,N2=2,N3=1;那么
1 2 3
4 5
6
就是一种合法排列。
数据范围
1≤k≤5,学生总人数不超过30人。
思路:
按照要求,如果我们每次拿比上一个大的,那么必定可以形成合法排列,即我们从小到大去选人排列。然后在状态转移的时候,对于每一个可以放的位置,那么把这个人放进去之后,这个状态的方案数=这个状态的方案数+原来状态的方案数。
那么如何判断一个位置是否可以放人呢?
因为最多五层,且总人数不超过30人,那么我们直接对每个状态进行枚举,在当前状态合法的情况下,判断当前状态可以由哪些状态转移过来,把合法的累加进去即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int mod=1e9+7;
const int INF=0x7fffffff;
const ll LLINF=0x7fffffffffffffff;
const double EPS=1e-10;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define int long long
int dp[33][33][33][33][33];
signed main()
{
IOS;
//freopen("","r",stdin);
//freopen("","w",stdout);
int k;
while(cin>>k&&k)
{
memset(dp,0,sizeof dp);
int kk[10]={0};
dp[0][0][0][0][0]=1;
for(int i=1;i<=k;i++)
{
cin>>kk[i];
}
for(int i=0;i<=kk[1];i++)
{
for(int j=0;j<=min(i,kk[2]);j++)
{
for(int k=0;k<=min(j,kk[3]);k++)
{
for(int l=0;l<=min(k,kk[4]);l++)
{
for(int m=0;m<=min(l,kk[5]);m++)
{
if(i&&i-1>=j)
dp[i][j][k][l][m]+=dp[i-1][j][k][l][m];
if(j&&j-1>=k)
dp[i][j][k][l][m]+=dp[i][j-1][k][l][m];
if(k&&k-1>=l)
dp[i][j][k][l][m]+=dp[i][j][k-1][l][m];
if(l&&l-1>=m)
dp[i][j][k][l][m]+=dp[i][j][k][l-1][m];
if(m)
dp[i][j][k][l][m]+=dp[i][j][k][l][m-1];
}
}
}
}
}
cout<<dp[kk[1]][kk[2]][kk[3]][kk[4]][kk[5]]<<endl;
}
}