找到满足给定条件的排列
Posted
技术标签:
【中文标题】找到满足给定条件的排列【英文标题】:Finding the permutation that satisfy given condition 【发布时间】:2017-02-17 13:45:24 【问题描述】:我想找出n
number 的所有排列的个数。编号将从1
到n
。给定条件是每个ith
position 最多可以有Si
的编号,其中Si
是为数字的每个位置提供的。
1<=n<=10^6
1<=si<=n
例如:
n=5
那么它的所有五个元素将是
1,2,3,4,5
给定每个位置的 Si 为:
2,3,4,5,5
它表明在:
1st
位置可以有1 to 2
即1,2
,但不能是3 to 5
中的数字。
相似地,
在2nd
位置只能有编号1 to 3
。
在3rd
位置只能有编号1 to 4
。
在4th
位置只能有编号1 to 5
。
在5th
位置只能有编号1 to 5
。
它的一些排列是:
1,2,3,4,5
2,3,1,4,5
2,3,4,1,5 etc.
但这些不可能:
3,1,4,2,5 As 3 is present at 1st position.
1,2,5,3,4 As 5 is present at 3rd position.
我不知道在给定条件下计算所有可能的排列数量。
【问题讨论】:
这是一些编程比赛吗?还是裁判问题? 它不属于任何正在运行的编码竞赛。但它需要我的编码实现。 @coderredoc 已经提到了 n 和 Si 的给定限制。 您需要只需要排列的数量 还是排列本身?对于您的示例,究竟有多少排列是可能的? @Yuriy 只有排列的次数。 【参考方案1】:好的,如果我们保证数字 si
以不降序的顺序给出,那么看起来可以计算 O(n)
中的排列数。
直截了当的算法思路如下:
-
在步骤
i
将结果乘以si[i]
的当前值;
我们为位置i
选择了一些数字。只要我们需要排列,这个数字就不能重复,所以将所有其余的si[k]
其中k
从i+1
到末尾(例如n
)减1;
将i
增加 1,返回 (1)。
以si: 2 3 3 4
为例说明:
result = 1
;
当前si
为“2 3 3 4”,result *= si[0]
(= 1*2 == 2),将 3、3 和 4 减 1;
当前si
为“..2 2 3”,result *= si[1]
(= 2*2 == 4),最后 2 和 3 减 1;
当前si
为“....1 2”,result *= si[2]
(= 4*1 == 4),将最后一个数字减1;
当前si
是“..... 1”,result *= si[3]
(= 4*1 == 4),完成。
但是,由于步数减少,这种简单的方法需要O(n^2)
。为了优化它,我们可以很容易地观察到,在 result *= si[i]
时刻,我们的 si[i] 已经精确地减少了 i
倍(当然假设我们从 0 开始)。
因此O(n)
方式:
unsigned int result = 1;
for (unsigned int i = 0; i < n; ++i)
result *= (si[i] - i);
【讨论】:
【参考方案2】:对于每个 si 计算数组中元素的数量,使得 a[i]
【讨论】:
【参考方案3】:要完成 Yuriy Ivaskevych 的回答,如果您不知道 sis 是否按递增顺序排列,您可以对 sis 进行排序,它也可以工作。 如果排列是不可能的,结果将为空或负(例如:1 1 1 1 1)
【讨论】:
【参考方案4】:您可以尝试回溯,这是一种有点硬核的方法,但会奏效。 尝试: http://www.thegeekstuff.com/2014/12/backtracking-example/ 或者google回溯教程C++
【讨论】:
n 最大为 1e6,这会得到 TLE,即使 O(n^2) 也会得到 TLE。 @Alex Banu,我对回溯知之甚少,请解释如何针对这个问题实施回溯。以上是关于找到满足给定条件的排列的主要内容,如果未能解决你的问题,请参考以下文章