思路:
题目的主要信息:
- 数组a表示n个人,记忆的他们原来的位置左边人数减去右边人数的绝对值
- 求原来有多少种排法
我们可以发现一个人的时候,,两个人的时候,,三个人的时候,,四个人的时候,或者其打乱了顺序,我们可以根据数学归纳法判断,若是为奇数,则数组a排序后应为,若是为偶数,则数组a排序后应为,其中数字0一定在中央,其他数字可以分别对换一次位置,因此总方案数为
方法一:排序+快速幂
具体做法:
首先对数组进行排序,然后根据奇偶人数检查数组是否是上述的形式,如若不是则无法构成正确的队列,如果是则根据公式,利用快速幂算出答案。
快速幂的解析如下:如果我们要计算,常规的算法是,然后再,如此往下,一共是次运算,即次。但是我们可以考虑这样:(二次)、(四次)、(八次),这是一个二分的思维,运算次数缩减到了次,公式如下:
class Solution { public: long long Pow(long long x, long long y){ //快速幂 long long res = 1; while(y){ if(y & 1) //可以再往上乘一个 res = res * x % 1000000007; x = x * x % 1000000007; //叠加 y = y >> 1; //减少乘次数 } return res; } int solve(int n, vector<int>& a) { if(n == 1) return a[0] == 0; sort(a.begin(), a.end()); //排序 int num = 0, index = 0; //表示开始数字及下标 if(n % 2 == 1){ //奇数 if(a[0] != 0) //第一个数必须为0 return 0; num = 2; index = 1; }else{ //偶数 num = 1; index = 0; } while(index < n){ //检查是否是合理的数组 if(a[index] != num || a[index + 1] != num) return 0; index += 2; num += 2; } return (int)Pow((long long)2, (long long)n / 2); //快速幂公式求结果 } };
复杂度分析:
- 时间复杂度:,sort函数排序耗费,检查数组是否合法为,公式计算为
- 空间复杂度:,常数级空间,无额外变量
方法二:数字统计法
具体做法:
我们找到前面的规律,除了奇数的数字0只出现一次外,其他的小于n的偶数会在n为奇数时都出现两次,其他的小于n的奇数会在n为偶数时都出现两次,我们可以利用这个规律,借助一个辅助数组统计所有数字出现的次数,根据奇偶检验该数字是否出现2次或者0次即可。最后的结果也是,我们只需要在偶数次时乘2即可。
class Solution { public: int solve(int n, vector<int>& a) { int mod = 1e9 + 7; int res = 1; vector<int> count(n, 0); //统计每个数字出现次数 for(int i = 0; i < n; i++) count[a[i]]++; if(n % 2){ //奇数个人 for(int i = 0; i < n; i++){ if(i == 0){ //奇数的第一个是0 if(count[0] != 1) return 0; } else{ if(i % 2 && count[i] != 0) //一定没有出现过 return 0; if(i % 2 == 0 && count[i] != 2) //一定出现2次 return 0; if(i % 2 == 0) res = res * 2 % mod; //2^(n/2),偶数次才乘2 } } }else{ //偶数个人 for(int i = 0; i < n; i++){ if(i % 2 == 0 && count[i] != 0) return 0; if(i % 2 && count[i] != 2) return 0; if(i % 2 == 0) res = res * 2 % mod;//2^(n/2),偶数次才乘2 } } return res; } };
复杂度分析:
- 时间复杂度:,不管是统计数字还是检验是否合法都是,计算在检验的循环中
- 空间复杂度:,辅助数组count的长度