823. 带因子的二叉树
做这题我的心中是十分难受的,sad,花了4个小时,没有通过。
主要是遇到https://leetcode-cn.com/submissions/detail/10936917/testcase/这个测试点过不了。
我觉得是取余那一块有点问题,但自认为逻辑是没有错的。也就是下面的代码是没有AC的代码。
题目描述:
给出一个含有不重复整数元素的数组,每个整数均大于 1。
我们用这些整数来构建二叉树,每个整数可以使用任意次数。
其中:每个非叶结点的值应等于它的两个子结点的值的乘积。
满足条件的二叉树一共有多少个?返回的结果应模除 10 ** 9 + 7。
示例 1:
输入: A = [2, 4]
输出: 3
解释: 我们可以得到这些二叉树: [2], [4], [4, 2, 2]
示例 2:
输入: A = [2, 4, 5, 10]
输出: 7
解释: 我们可以得到这些二叉树: [2], [4], [5], [10], [4, 2, 2], [10, 2, 5], [10, 5, 2].
提示:
1 <= A.length <= 1000.
2 <= A[i] <= 10 ^ 9
.
题意:
看示例就可以明白并不是要真的建一颗树,而是找出符合要求的排列组合。单个因子也算是符合要求。
以示例2为例,有7个组合符合要求。其中,2作为父节点的出现1次,4作为父节点出现两次,5作为父节点出现1次,10作为父节点出现3次。
所有的输出就是各个元素作为父节点出现的次数之和。
问题转换为求每个元素作为父节点的次数。
算法:
先看示例2:
看元素4,它除了有单个元素作为一种答案,还有[4,2,2]这样。所以总的次数变为了1+1=2;
看元素10它,除了有单个元素作为一种答案,还有[10,2,5], [10,5,2]。总的次数变为了1+2=3.
上面1次就差在4的两个子节点一样,而10的两个子节点不一样,可以调换位置。
示例2能得到的信息貌似就这么多了,自己再想个示例,[2,4,5,8]。这个示例的输出个数好像会比示例2多。
[2,4,5,8]穷举一下,看看有哪几种。[2], [4], [5], [8]; [4,2,2], [8,4,2], [8,2,4]; [8,4,2,2,2]; [8,2,4,2,2],以上组合都以层次遍历的方式排列。
统计下,一共有9次输出,以2为父节点1次,4为父节点2次,5为父节点1次,8为父节点5次。
研究下元素8吧:
它的子节点从2,4中取,那会不会和(以2为父节点的次数1次)、(以4为父节点2次)这个两个数字有关。
好像是以8为父节点的次数5 = 1 * 2 * 2 + 1;实际上这就是我找的规律。
等式右边一共有4个数字,
第一个数字1:是以2为父节点的次数;
第二个数字2:是以4为父节点的次数;
第三个数字2: 它是一个参数,当父节点两边的因子一样时,它取1,如示例2中,父节点4出现的次数2 = 1 * 1 * 1 + 1;不一样时,它取2;
第四个数字1:它是父节点8被赋的初始值。
可以随便想个示例,看看上面这个规律是不是适用。
思路:
- 对输入数组A从小到大排序,方便顺序遍历
-
用一个map容器记录m元素作为父节点出现的次数。对A遍历一遍,每个元素加入到m中,并且赋初值为1。
- 主函数是两个for循环,进行m中值的更新,所有更新不是一步到位。比如一开始某个元素parent通过它的一个child更新了,后来child出现的次数增加了,
parent的次数也要作相应增加。
通过文字想表达清楚意思实在太难了,画图工程量太大。直接上我的代码吧,只要不要像一开始的那种程度的测试点,应该都能过。
1 #include <iostream> 2 #include <vector> 3 #include <cmath> 4 #include <algorithm> 5 #include <map> 6 using namespace std; 7 8 class Solution { 9 public: 10 long long INF; 11 map<int, vector<int> > father; //map<a, vec> ;vec存储的是以a为乘子的A中元素 12 map<int, unsigned long long> m; 13 14 int numFactoredBinaryTrees(vector<int>& A) { 15 //paremeters 16 int backSum = 0, len = A.size(); 17 INF = pow(10, 9) + 7; 18 //init 19 sort(A.begin(), A.end()); 20 for (int i = 0; i < len; i++) 21 m[A[i]] = 1; 22 23 //process 24 for (int i = 0; i < len; i++) 25 { 26 for (int j = i; j < len; j++) 27 { 28 unsigned long long mul = A[i] * A[j]; 29 unsigned long long more; 30 31 if(m.find(mul) != m.end()) //两者的乘积也在数组A中 32 { 33 father[A[i]].push_back(mul); //A[i]是父亲mul的乘子 34 if(i == j) 35 { 36 more = m[A[i]] * m[A[j]]; //more是mul次数应该增加的部分 37 } 38 else 39 { 40 father[A[j]].push_back(mul); //A[j]是父亲mul的乘子 41 more = m[A[i]] * m[A[j]] * 2; //more是mul次数应该增加的部分 42 } 43 44 m[mul] += more; 45 updateFatherOf(mul, more); //mul已经更新了,顺便也要更新以它的乘子的父亲们 46 } 47 } 48 } 49 50 int cnt = 0; 51 //output 52 for(auto i : m) 53 { 54 if(cnt < len) 55 { 56 cnt++; 57 cout << i.first << ":" << i.second; 58 if(cnt % 10 == 0) 59 cout << endl; 60 else 61 cout << ' '; 62 } 63 int f; 64 if(i.second >= INF) 65 f %= INF; 66 else 67 f = i.second; 68 69 backSum %= INF; 70 backSum += f; 71 } 72 return backSum; 73 } 74 75 void updateFatherOf(int x, unsigned long long exceed) 76 { 77 for(auto f : father[x]) 78 { 79 int another = f / x; 80 if (another == x) 81 { 82 m[f] += exceed * exceed; 83 updateFatherOf(f, exceed * exceed); //f是x的父亲之一,f更新了,要以f为乘子对f所有的父亲进行更新 84 } 85 else 86 { 87 m[f] += 2 * exceed * m[another]; 88 updateFatherOf(f, 2 * exceed * m[another]); 89 } 90 } 91 } 92 }; 93 94 int main() 95 { 96 Solution s; 97 int a[] = {2, 4, 6, 8}; 98 99 vector<int> vec(a, a + sizeof(a)/sizeof(int)); 100 cout << "满足条件的二叉树一共有:" << endl << s.numFactoredBinaryTrees(vec) << endl; 101 return 0; 102 }