题意:
输入每一排的人数,保证前一排的人数大于后一排,要求每一排从左到右编号递增,每一列从前往后编号递增,输出方案数。最多5排,最多30个编号。
题解:
显然可以使用动态规划。用 f [ i 1 ] [ i 2 ] [ i 3 ] [ i 4 ] [ i 5 ] f[i_1][i_2][i_3][i_4][i_5] f[i1][i2][i3][i4][i5]表示第1排有 i 1 i_1 i1个人,第2排有 i 2 i_2 i2个人,第3排有 i 3 i_3 i3个人,第4排有 i 4 i_4 i4个人,第5排有 i 5 i_5 i5个人时的方案数。如果没有那么多排,直接当成这一排最多有0人即可。
动态规划的精髓就是转移方程,我们经常想的是当前状态可以从哪些状态转移而来,其实也可以考虑当前状态可以转移到哪些状态。对于 f [ i 1 ] [ i 2 ] [ i 3 ] [ i 4 ] [ i 5 ] f[i_1][i_2][i_3][i_4][i_5] f[i1][i2][i3][i4][i5],如果新增一个人,那么
- 这一排的人数还没有满
- 如果不插在第1排,那么就要求这一排的人数小于前一排。否则之后一定在前面一排插入人,就会导致前排的人的序号大于后排的。
AC代码:
#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <algorithm>
#include <cmath>
#include <set>
#include <map>
#include <iomanip>
#include <cstdlib>
#define pb push_back
#define fir first
#define sec second
#define All(x) x.begin(),x.end()
#define ms(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define lep(i,a,b) for(int i=(a);i>=(b);i--)
#define INF 0x3f3f3f3f
#define multi int T;scanf("%d",&T);while(T--)
using namespace std;
typedef long long ll;
typedef double db;
const int N=35;
const int mod=10007;
const db pi=acos(-1.0);
int a[7],n;
ll f[N][N][N][N][N];
int main()
{
#ifndef ONLINE_JUDGE
freopen("D:\\work\\data.in","r",stdin);
#endif
int k,sum=0;
while(scanf("%d",&k),k){
for(int i=1;i<=k;i++) scanf("%d",&a[i]);
for(int i=k+1;i<=5;i++) a[i]=0;
rep(i1,0,a[1])
rep(i2,0,a[2])
rep(i3,0,a[3])
rep(i4,0,a[4])
rep(i5,0,a[5]) f[i1][i2][i3][i4][i5]=0;
f[0][0][0][0][0]=1;
rep(i5,0,a[5])
rep(i4,0,a[4])
rep(i3,0,a[3])
rep(i2,0,a[2])
rep(i1,0,a[1]){
if(i5<a[5]&&i5<i4)
f[i1][i2][i3][i4][i5+1]+=f[i1][i2][i3][i4][i5];
if(i4<a[4]&&i4<i3)
f[i1][i2][i3][i4+1][i5]+=f[i1][i2][i3][i4][i5];
if(i3<a[3]&&i3<i2)
f[i1][i2][i3+1][i4][i5]+=f[i1][i2][i3][i4][i5];
if(i2<a[2]&&i2<i1)
f[i1][i2+1][i3][i4][i5]+=f[i1][i2][i3][i4][i5];
if(i1<a[1])
f[i1+1][i2][i3][i4][i5]+=f[i1][i2][i3][i4][i5];
}
printf("%lld\n",f[a[1]][a[2]][a[3]][a[4]][a[5]]);
}
}