题意:已知一个k行每行有a[i](1 <= i <= k)个人的队列 给n个身高分别为1~n的人安排队列 要求队列从左往右身高递减 从后往前身高递减 1表示最高 n表示最矮 求方案数
书上说可以用dp的思路做 然而写出来MLE ????
方法挺好 dp[z1][z2][z3][z4][z5]表示第1-5行的人数
如果z1<=a[1] 就可以 通过 dp[z1][z2][z3][z4][z5] += dp[z1-1][z2][z3][z4][z5] 转移 以此类推
dp代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <map>
#include <cstdlib>
#include <cstring>
using namespace std;
long long dp[31][31][31][31][31];
int n;
int a[33];
int main()
{
int k;
while(cin >> k&&k)
{
n = 0;
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
for(int i = 1; i <= k; i++)
{
cin >> a[i];
n += a[i];
}
dp[0][0][0][0][0] = 1;
for(int z1 = 1; z1 <= a[1]; z1++)
{
for(int z2 = 0; z2 <= a[2]&&z1+z2<=n&&z1>=z2; z2++)
{
for(int z3 = 0; z3 <= a[3]&&z1+z2+z3<=n&&z2>=z3; z3++)
{
for(int z4 = 0; z4 <= a[4]&&z1+z2+z3+z4<=n&&z3>=z4; z4++)
{
for(int z5 = 0; z5 <= a[5]&&z1+z2+z3+z4+z5<=n&&z4>=z5; z5++)
{
dp[z1][z2][z3][z4][z5] += dp[z1-1][z2][z3][z4][z5];
if(z2>0)
{
dp[z1][z2][z3][z4][z5] += dp[z1][z2-1][z3][z4][z5];
}
if(z3>0)
{
dp[z1][z2][z3][z4][z5] += dp[z1][z2][z3-3][z4][z5];
}
if(z4>0)
{
dp[z1][z2][z3][z4][z5] += dp[z1][z2][z3][z4-1][z5];
}
if(z5>0)
{
dp[z1][z2][z3][z4][z5] += dp[z1][z2][z3][z4][z5-1];
}
}
}
}
}
}
cout<<dp[a[1]][a[2]][a[3]][a[4]][a[5]]<<endl;
}
}
正解是杨氏矩阵+钩子定理 算是科普了 挺好用的
杨氏矩阵:
杨氏矩阵定义(需满足的条件/特征):
(1)若格子(i,j)无元素,则该格子的右边和上边一定没有元素;
(2)若格子(i,j)有元素data[i][j],则该格子右边和上边相邻的格子要么没有元素,要么有比data[i][j]大的元素。
因为题目中给出的身高1是最高的 所以可以看成一个水平翻转的杨氏矩阵
钩子定理(在杨氏矩阵中):
用来求杨氏矩阵个数的算法
钩子长度:右边元素个数+上边元素个数+1
钩子公式:对于给定形状,不同的杨氏矩阵的个数为(n!/(每个格子的钩子长度加1的积))。
然后直接算答案就行了 注意n!可能会溢出 所以要边乘边约分
AC代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <map>
#include <cstdlib>
#include <cstring>
using namespace std;
long long dp[31][31][31][31][31];
int n;
int a[33];
int ju[33][33];
long long gcd(long long a,long long b)
{
if(a==0)
{
return b;
}
else
return gcd(b%a,a);
}
int main()
{
int k;
while(cin >> k&&k)
{
int n = 0;
for(int i = 1; i <= k; i++)
{
cin >> a[i];
n += a[i];
}
for(int i = 1; i <= k; i++)
{
for(int j = 1; j <= a[i]; j++)
{
int num = 0;
for(int z = i + 1; z <= k; z++)
{
if(a[z] >= j)
{
num++;
}
}
ju[i][j] = num + a[i] - j + 1;
}
}
long long ans = 1;
long long ans2 = 1;
for(int i = 1; i <= k; i++)
{
for(int j = 1; j <= a[i]; j++)
{
ans *= n;
n--;
ans2 *= ju[i][j];
int g = gcd(ans,ans2);
ans /= g;
ans2 /= g;
}
}
cout<<ans/ans2<<endl;
}
}