Evanyou Blog 彩带

(转载)杨氏矩阵与勾长公式

转载:

杨氏矩阵详解:ORZ

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

 

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

(2)如果格子(i,j)有元素a[i][j],则它右边和上边的相邻格子要么没有元素,要么有元素且比a[i][j]大。

 

1 ~ n所组成杨氏矩阵的个数可以通过下面的递推式得到:

 

 

如图就是n=3时的杨氏矩阵。

 

 

 

 

下面介绍一个公式,那就是著名的钩子公式。

 

对于给定形状,不同的杨氏矩阵的个数为:n!除以每个格子的钩子长度加1的积。其中钩子长度定义为该格子

右边的格子数和它上边的格子数之和。

 

题目:http://poj.org/problem?id=1825

 

 

介绍完了钩子公式,那么我们可以来做一道基础题了。

 

题目:给四行,第一行放5个数字,第二行放三个数字,第三行放3个数字,第四行放1个数字,都是左对齐的排列,

     现有1~12共12个数字,要求放到这四行中,从上到下,从左到右都是按小到大排列,问你共有几种排法?


() () () () ()

() () ()

() () ()

()

 

这个问题直接利用钩子公式解决即可。

 

 

杨氏矩阵既可以用来当堆,又可以当成平衡树。通常杨氏矩阵会涉及到两个问题:

 

(1)在杨氏矩阵中查找值为x的元素      (2)在杨氏矩阵中找第K大的元素

 

对于第一个问题,其实有两种方法,第一种方法就是二分查找法,这种方法的时间效率不是很好。第二种方法就是类

堆查找法。方法是这样的:从矩阵的右上角出发,对于元素a[i][j],如果a[i][j]==x,则找到元素x,直接返

回; 如果a[i][j]> x,则向下移动,即继续比较a[i+1][j]与x;如果a[i][j] < x,则向左移动,即继续比

较a[i][j-1]与x。该算法的时间复杂度是O(m+n)。

 

复制代码
  1. 1 bool Find(int a[][N],int n,int m,int x)
  2. 2 {
  3. 3 assert(a != NULL && n > 0 && m > 0);
  4. 4 int row = 0;
  5. 5 int col = m - 1;
  6. 6 while(row <= n - 1 && col >= 0)
  7. 7 {
  8. 8 if(a[row][col] == x) return true;
  9. 9 else if(a[row][col] > x) col--;
  10. 10 else row++;
  11. 11 }
  12. 12 return false;
  13. 13 }
复制代码

 

对于第二个问题,首先,二分枚举找到一个数x,它比杨氏矩阵中k个数大;然后,利用类堆查找法找到刚好小于x的

元素。该算法的时间复杂度为O((m+n)log(mn)),但不需要额外存储空间。

 

复制代码
  1. 1 int get_order(int a[][N],int n,int m,int k)
  2. 2 {
  3. 3 int row = 0;
  4. 4 int col = m - 1;
  5. 5 int order = 0;
  6. 6 while(row <= n - 1 && col >= 0)
  7. 7 {
  8. 8 if(a[row][col] < k)
  9. 9 {
  10. 10 order += col + 1;
  11. 11 row++;
  12. 12 }
  13. 13 else col--;
  14. 14 }
  15. 15 return order;
  16. 16 }
  17. 17
  18. 18 int Find_Kth_Num(int a[][N],int n,int m,int k)
  19. 19 {
  20. 20 int low = a[0][0];
  21. 21 int high = a[n-1][m-1];
  22. 22 int order = 0;
  23. 23 int mid = 0;
  24. 24 do
  25. 25 {
  26. 26 mid = (low + high) >> 1;
  27. 27 order = get_order(a,n,m,mid);
  28. 28 if(order == k) break;
  29. 29 else if(order > k) high = mid - 1;
  30. 30 else low = mid + 1;
  31. 31 }while(1);
  32. 32 int row = 0;
  33. 33 int col = m - 1;
  34. 34 int ret = mid;
  35. 35 while(row <= n - 1 && col >= 0)
  36. 36 {
  37. 37 if(a[row][col] < mid)
  38. 38 {
  39. 39 ret = max(ret,a[row][col]);
  40. 40 row++;
  41. 41 }
  42. 42 else col--;
  43. 43 }
  44. 44 return ret;
  45. 45 }
复制代码

 

转载:

首先,我们来看一个最简单的问题:

我在学校门口卖奶茶,奶茶一元一杯。今天下午开门的时候,我发现找零的钱忘带了。

这时候来了 2n 个人,其中 n 个人身上只有一张一元钱,另外 n 个人身上只有一张两元钱。我就让他们排成一队,然后用这 n 个人的一元钱来找给付两元的人。当然,排队的时候得保证每次来一个付两元的人的时候都有的找。

假设所有拿一元的人和拿两元的人都没有分别,我现在想知道,他们有多少种排队方式?

这个问题的答案大家都知道,是 Cat[n],即第 n 个卡特兰数(Catalan number)。不过我现在的问题是如下的升级问题。

升级1:条件同上,但这时候来的人数为 3n ,其中 n 个人只有一张一元钱,n 个只有一张两元钱, n 个只有一张三元钱(假设题设的每种面值的钞票均存在)。我仍然让他们排成一队,只要有付两元的就用一元找,付三元的就用两元找。同样得保证每当需要找钱时有对应的钱可以找。求他们有多少种排队方式?

以及最终问题

升级2:条件同上,但这时候来的人数为 mn,其中拥有面值为一元至 m 元的人均有 n 个。每当支付 k (1<k\leq m)元时用 k-1 面值的钞票去找零。求合法排队方式数。

 

先看例题:【HihoCoder1480:矩阵填数 】

题意:将N*M个整数填入N*M的矩阵中,要求当前位置的数小于左边和上面的数,求方案数。

有【钩子公式】:

 对于给定形状,不同的杨氏矩阵的个数为:n!除以每个格子的钩子长度加1的积。 其中钩子长度定义为该格子 右边的 格子数和 它上边的格子数之和。

 则易得此题公式:

  1. ans=fac[n*m];
  2. for(int i=1;i<=n;i++)
  3. for(int j=1;j<=m;j++){
  4. ans=ans*rev[i+j-1]%Mod;
  5. }

 

【关键】:这个矩阵填数和卖奶茶的关系:

加入3*N个人买奶茶: N个一块钱的+N个两块钱+N个三块钱,给他们的排队队伍编号1,2,3 ...  3*N。然后把这3*N个人拿去填充矩阵。

具体的,一块钱的在第一排,两块钱的在第二排,三块钱的在第三排。 那么他们必须满足:先来的在左边,小面额的在上边。

即当前位置是数小于上面的和左边的。这样以来这个解就可以用钩子定理来求解了。

 

 

此外:

杨氏矩阵还有递推公式:f[n]=f[n-1]+(n-1)*f[n-2]。

还可以用卡特兰数表示:第(n-1)*(m-1)+1个卡特兰数。

 

posted @   five20  阅读(3069)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· DeepSeek火爆全网,官网宕机?本地部署一个随便玩「LLM探索」
· 开发者新选择:用DeepSeek实现Cursor级智能编程的免费方案
· 【译】.NET 升级助手现在支持升级到集中式包管理
· 独立开发经验谈:如何通过 Docker 让潜在客户快速体验你的系统
· Tinyfox 发生重大改版
Live2D
欢迎阅读『(转载)杨氏矩阵与勾长公式』
点击右上角即可分享
微信分享提示