POJ2279 Mr Young's Picture Permutations
描述: 有N个学生合影,站成左对齐的k排,每行分别有N1,N2…NK个人,第一排站最后,第k排站之前。
学生身高依次是1…N。在合影时候要求每一排从左到右递减,每一列从后面到前也递减,一共有多少总方案
输入
每组测试数据包含两行。第一行给出行数k。第二行包含从后到前(n1,n2,…,nk)的行的长度,作为由单个空格分隔的十进制整数。
问题数据以0结束。
N<=30, k<=5;
输出 输出每组数据的方案数
样例输入
1
30
5
1 1 1 1 1
3
3 2 1
4
5 3 3 1
5
6 5 4 3 2
2
15 15
0
样例输出
1
1
16
4158
141892608
9694845
- 法一: dp
用一个k元组来表示每一行已经确定的人数即可描述一个状态,进行转移即可。
tip:从本题中可知,设计动态规划的状态转移方程不一定要以如何计算出一个状态的形式给出,也可以考虑用一个已知的状态更新后续阶段的状态
1 #include <cstdio>
2 #include <iostream>
3 #include <cstring>
4 using namespace std;
5 long long dp[31][16][11][9][6];//空间注意计算,否则会开爆(31,31/2,31/3...)
6 //f(a1,a2,a3,a4,a5)表示第i层有a[i]个人时的方案数
7 int k,num[6];
8 int main()
9 {
10 while(scanf("%d",&k) && k)
11 {
12 memset(dp,0,sizeof(dp));
13 memset(num,0,sizeof(num));
14 for(int i=1 ; i<=k ; i++) scanf("%d",&num[i]);
15 dp[0][0][0][0][0]=1;
16 for(int i=0 ; i<=num[1] ; i++)
17 {
18 for(int j=0 ; j<=num[2] ; j++)
19 {
20 for(int k=0 ; k<=num[3] ; k++)
21 {
22 for(int l=0 ; l<=num[4] ; l++)
23 {
24 for(int p=0 ; p<=num[5] ; p++)
25 {
26 if(i+1<=num[1])
27 dp[i+1][j][k][l][p]+=dp[i][j][k][l][p];
28 if(j+1<=num[2]&&j<i)
29 dp[i][j+1][k][l][p]+=dp[i][j][k][l][p];
30 if(k+1<=num[3]&&k<j&&k<i)
31 dp[i][j][k+1][l][p]+=dp[i][j][k][l][p];
32 if(l+1<=num[4]&&l<k&&l<j&&l<i)
33 dp[i][j][k][l+1][p]+=dp[i][j][k][l][p];
34 if(p+1<=num[5]&&p<l&&p<k&&p<j&&p<i)
35 dp[i][j][k][l][p+1]+=dp[i][j][k][l][p];
36
37 }
38 }
39 }
40 }
41 }
42 printf("%lld
",dp[num[1]][num[2]][num[3]][num[4]][num[5]]);
43 }
44 return 0;
45 }
- 法二:(数学解法)杨氏矩阵和勾长公式
转载:巨佬博客
杨氏矩阵又叫杨氏图表,它是这样一个矩阵,满足条件:
(1)如果格子(i,j)没有元素,则它右边和上边的相邻格子也一定没有元素。
(2)如果格子(i,j)有元素a[i][j] a[i][j]a[i][j],则它右边和上边的相邻格子要么没有元素,要么有元素且比a[i][j] a[i][j]a[i][j]大。
1 ~ n所组成杨氏矩阵的个数可以通过下面的递推式得到:如图就是n=3时的杨氏矩阵。
下面介绍一个公式,那就是著名的钩子公式。
对于给定形状,不同的杨氏矩阵的个数为:n!除以每个格子的钩子长度加1的积。其中钩子长度定义:每个格子右边的格子数和它上边的格子数之和。
代码:
1 #include <cstdio>
2 #include <iostream>
3 #include <cstring>
4 using namespace std;
5
6 inline int gcd(int a,int b)
7 {
8 return (b==0)?a:gcd(b,a%b);
9 }
10 int d[100],num[100];
11 int n;
12 int main()
13 {
14 while(scanf("%d",&n)&&n)
15 {
16 int tot=0;
17 memset(num,0,sizeof(num));
18 for(int i=1 ; i<=n ; i++) scanf("%d",&d[i]);
19 for(int i=n ; i>=1 ; i--)
20 for(int j=1 ; j<=d[i] ; j++)
21 {
22 tot++;
23 for(int k=i+1 ; k<=n ; k++)
24 if(d[k]>=j) num[tot]++;
25 else break;
26 num[tot]+=d[i]-j+1;
27 }
28 long long t1=1,t2=1;
29 for(int i=1 ; i<=tot ; i++)
30 {
31 t1*=i; t2*=num[i];
32 int t=gcd(t1,t2);
33 t1/=t,t2/=t;
34 }
35 printf("%lld
",t1/t2);
36 }
37 return 0;
38 }