我想找出n
数字的所有排列的数量。数字将从1
到n
。给定的条件是每个ith
位置都可以号码最多为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.
我不知道在给定条件下计算所有可能的排列数。
答案 0 :(得分:2)
好的,如果我们保证在而不是降序顺序中给出数字si
,那么看起来可以计算O(n)
中的排列数。 / p>
直接算法的想法如下:
i
将结果乘以si[i]
的当前值; i
选择了一些号码。只要我们需要排列,这个数字就不能重复,所以其余si[k]
将k
从i+1
到结尾(例如n
)减少1; < / LI>
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);
}
答案 1 :(得分:0)
对于每个si计数数组中元素的数量,使得a [i]&lt; = si使用二进制搜索,并将值存储到数组count [i],现在答案是所有计数的乘积[ i],但是我们减少了答案中的冗余数量(因为相同的数字可以计数两次),因为你可以对si进行排序并检查多少个数字是&lt; = s [i],然后减少每个数字算,复杂度是O(nlog(n)),希望至少我给你一个想法。
答案 2 :(得分:0)
要完成Yuriy Ivaskevych的回答,如果您不知道sis是否按顺序增加,您可以对sis进行排序,它也可以。 如果排列是不可能的,结果将为空或否定(例如:1 1 1 1 1)
答案 3 :(得分:-1)
你可以尝试回溯,这是一个小硬核方法,但会起作用。 尝试: http://www.thegeekstuff.com/2014/12/backtracking-example/ 或google回溯教程C ++