POJ2279 Mr. Young's Picture Permutations

POJ2279 Mr. Young’s Picture Permutations 题解

题目请戳这里: Mr. Young’s Picture Permutations

这道题有两种解法。

解法1:动态规划

这道题即满足最优子结构,又满足无后效性,显然可以用动态规划求解。

我们可以考虑,因为这里的矩阵最多只有五行,我们可以用 N 1 ⋯ N 5 N_1 \cdots N_5 N1N5 来表示每行的人数, 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<ti1,那么状态就可以转移:
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:杨氏矩阵+勾长定理

首先先介绍一下这两个东西:

  • 杨氏矩阵

    杨氏矩阵又叫杨氏图表,它是这样一个矩阵,满足条件:

    1. 如果格子 ( i , j ) (i,j) (i,j) 没有元素,则它右边和上边的相邻格子也一定没有元素。

    2. 如果格子 ( 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(n1)+(n1)×F(n2)(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的积} 1n!

    其中钩子长度定义为该格子右边的格子数和它上边的格子数之和。

那么这题就是一个公式题了。

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值