1014 - 结论题&dp? - Mr. Young's Picture Permutations(POJ 2279)

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/83049665

传送门

题意

给出n行,每行有人数限制num[i],并且num[i]>=num[i+1],总人数暂且称为tot=∑num[i],把1~tot这些数字填入矩阵,使得矩阵满足每行单调递增,每列单调递增,求满足要求的矩阵数目

分析

一开始是打着做dp的名号,发现了这道题

在《算法竞赛》这本书上,是这样讲的

线性dp

但实际上,由于这道题空间限制比较严,我们dp的话会爆内存(上一篇博客有解释)

所以就凉了……

 网上盛传的做法是利用杨氏矩阵和钩子公式,我就去学习了一下

本来以为几行代码就搞定,是一道水题,后来发现还是要思考一下的

先来讲一下杨氏矩阵

  杨氏矩阵定义(需满足的条件/特征):

(1)若格子(i,j)没有元素,则该格子的右边和上边一定没有元素;

(2)若格子(i,j)有元素data[i][j],则该格子右边和上边相邻的格子要么没有元素,要么有比data[i][j]大的元素。

显然有同一些元素组成的杨氏矩阵不唯一,1~n组成杨氏矩阵的个数可以写出:

F[1]=1,F[2]=2,  F[n]=F[n-1]+(n-1)*F[n-2] (n>2)。

(这个东西不会证……但我们很容易发现这简直就是这道题的模样啊)

再来说一下钩子公式(wcr大佬表示非常喜欢这个名字??)

对于给定形状,不同的杨氏矩阵的个数为(n!/(每个格子的钩子长度加1的积))。

钩子长度:该格子右边的格子数和它上边的格子数之和;

然后就会发现这道题简直就是为这个公式的应用而生的

只是……

如果我们简单套公式的话就会出岔子

因为结合这道题,从前往后值是递增的,下面的数是大于当前数的,所以我们换一下,把下面看做上面(相当于把矩阵180度旋转一下)

还有一点由于和阶乘有关,我们很容易就炸掉了,所以要一边处理一边约分

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
int k,num[10];
ll sum[200];
ll gcd(ll x,ll y){
	ll z=x%y;
	while(z){x=y;y=z;z=x%y;}
	return y;
}
int main(){
	while(1){
		memset(sum,0,sizeof(sum));
		scanf("%d",&k);
		if(k==0) break;
		int i,j,n=0;
		for(i=1;i<=k;++i)	scanf("%d",&num[i]);
		for(i=1;i<=k;++i){
			for(j=1;j<=num[i];++j){
				++n;
				for(int p=i+1;p<=k;++p){
					if(num[p]>=j) sum[n]++;
					else break;
				}
				sum[n]+=num[i]-j+1;
			}
		}
		ll x=1,y=1;
		for(i=1;i<=n;++i){
			x*=i;y*=sum[i];
			ll tmp=gcd(x,y);
			x/=tmp;y/=tmp;
		}
		printf("%I64d\n",x/y);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/83049665