POJ2279 Mr. Young’s Picture Permutations 题解
题目请戳这里: Mr. Young’s Picture Permutations
这道题有两种解法。
解法1:动态规划
这道题即满足最优子结构,又满足无后效性,显然可以用动态规划求解。
我们可以考虑,因为这里的矩阵最多只有五行,我们可以用 N 1 ⋯ N 5 N_1 \cdots N_5 N1⋯N5 来表示每行的人数, f [ a 1 ] [ a 2 ] [ a 3 ] [ a 4 ] [ a 5 ] f[a_1][a_2][a_3][a_4][a_5] f[a1][a2][a3][a4][a5] 来表示状态的转换,那么最终的答案就是 f [ N 1 ] [ N 2 ] [ N 3 ] [ N 4 ] [ N 5 ] f[N_1][N_2][N_3][N_4][N_5] f[N1][N2][N3][N4][N5]。
又因为题目中要求这个矩阵要从上到下,从左到右逐渐变大,把么对于每一行
i
i
i,如果
t
i
<
N
i
ti<N_i
ti<Ni 并且
t
i
<
t
i
−
1
t_{i}<t_{i-1}
ti<ti−1,那么状态就可以转移:
f
[
1
]
.
.
.
[
N
i
+
1
]
.
.
.
[
5
]
+
=
f
[
1
]
.
.
.
[
N
i
+
1
]
.
.
.
[
5
]
f[1]...[N_i+1]...[5] += f[1]...[N_i+1]...[5]
f[1]...[Ni+1]...[5]+=f[1]...[Ni+1]...[5]
那么代码就是这个了
#include <bits/stdc++.h>
#define M 31
using namespace std;
int n, m, ans=0;
int a[6];
inline int read()
{
int re=0, f=1; char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9') {re=re*10+(ch-'0'); ch=getchar();}
return re*f;
}
int main()
{
while(~scanf("%d\n",&n) && n)
{
memset(a,0,sizeof(a));
for(int i=1;i<=n;++i) a[i]=read();
long long f[a[1]+1][a[2]+1][a[3]+1][a[4]+1][a[5]+1];
memset(f,0,sizeof(f));
f[0][0][0][0][0]=1;
for(int i5=0;i5<=a[5];++i5)
for(int i4=0;i4<=a[4];++i4)
for(int i3=0;i3<=a[3];++i3)
for(int i2=0;i2<=a[2];++i2)
for(int i1=0;i1<=a[1];++i1)
{
if(i1<a[1])
f[i1+1][i2][i3][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(i3<a[3] && i3<i2)
f[i1][i2][i3+1][i4][i5]+=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(i5<a[5] && i5<i4)
f[i1][i2][i3][i4][i5+1]+=f[i1][i2][i3][i4][i5];
}
printf("%lld\n",f[a[1]][a[2]][a[3]][a[4]][a[5]]);
}
return 0;
}
解法2:杨氏矩阵+勾长定理
首先先介绍一下这两个东西:
-
杨氏矩阵
杨氏矩阵又叫杨氏图表,它是这样一个矩阵,满足条件:
-
如果格子 ( i , j ) (i,j) (i,j) 没有元素,则它右边和上边的相邻格子也一定没有元素。
-
如果格子 ( i , j ) (i,j) (i,j) 有元素 a [ i ] [ j ] a[i][j] a[i][j],则它右边和上边的相邻格子要么没有元素,要么有元素且比 a [ i ] [ j ] a[i][j] a[i][j] 大。
1 1 1 到 n n n 所组成的杨氏矩阵可以用下面这个递推式求出:
F ( n ) = F ( n − 1 ) + ( n − 1 ) × F ( n − 2 ) ( n > 2 ) F(n) = F(n-1)+(n-1)\times F(n-2) (n>2) F(n)=F(n−1)+(n−1)×F(n−2)(n>2)
其中
F ( 1 ) = 1 , F ( 2 ) = 2 F(1)=1,F(2)=2 F(1)=1,F(2)=2 -
-
勾长定理
下面介绍一个公式,那就是著名的钩子公式。
对于给定形状,不同的杨氏矩阵的个数为: n ! 每 个 格 子 的 钩 子 长 度 加 1 的 积 \dfrac{n!} {每个格子的钩子长度加1的积} 每个格子的钩子长度加1的积n!。
其中钩子长度定义为该格子右边的格子数和它上边的格子数之和。
那么这题就是一个公式题了。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
ll a[10],tot=0,hook=1,ans=1;
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
void get_hook()
{
for(int i=n;i>=1;i--)
{
for(int j=1;j<=a[i];j++)
{
ll cnt=a[i]-j+1;
for(int k=n;k>i;k--) if(a[k]>=j) cnt++;
hook*=cnt; ans*=++tot;
ll gcdd=gcd(ans,hook);
ans/=gcdd; hook/=gcdd;
}
}
}
int main()
{
while(scanf("%d",&n))
{
if(n==0) break;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ans=1,hook=1,tot=0;
get_hook();
cout<<ans<<'\n';
}
return 0;
}