LeetCode-27

[Medium] 29. 两数相除

分析

  • 只能用加减法,最朴素的方法是循环相减/加,直到小于0/大于0,计算加/减的次数
  • 这样算法是o(n),考虑到i+=i或者i<<=1相当于i*=2,i>>=1相当于i/=2
  • 只考虑divisor, divident都大于0的情况,先找到整数p,使得 divisor2p<=dividentdivisor*2^p <= dividentdivident=divisor2p,ratio+=2pdivident-=divisor*2^p, ratio+=2^p若divident为0,则商为ratio,否则重复上面的过程,直到divident为0。
  • 考虑divisor, divident到可能正,可能负,而负数的范围大于正数,直接将所有整数变成负数,并记录符号
  • 注意取相反数的时候要用位运算~x+1

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public:
int divide(int dividend, int divisor) {
if(dividend < divisor && dividend > 0) return 0;
if(dividend > divisor && dividend < 0) return 0;
if((dividend == INT_MIN) && (divisor == -1)) return INT_MAX;
if((dividend == INT_MIN) && (divisor == 1)) return INT_MIN;
if(dividend == divisor) return 1;
if(dividend < 0 && divisor > 0 && dividend == ~divisor+1) return -1;
if(dividend > 0 && divisor < 0 && ~dividend+1 == divisor) return -1;
bool sign = false;
if(dividend < 0) {
sign = !sign;
} else {
dividend = ~dividend+1;
}
if(divisor < 0) {
sign = !sign;
} else {
divisor = ~divisor+1;
}
int res = 0;
int i = -1;
while(dividend < divisor && divisor >= (INT_MIN >> 1)) {
divisor += divisor;
i+=i;
}
while(true) {
while(dividend > divisor) {
if(i == -1) {
return sign ? (res) : (~res+1);
}
divisor >>= 1;
i>>=1;
}
dividend -= divisor;
res+=i;
}
}
};

275. H 指数 II

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int hIndex(vector<int>& citations) {
int n = citations.size();
int l = 0, r = n-1;
int res = 0;
while(l < r) {
int mid = (r - l) / 2 + l;
if(n - mid <= citations[mid]) {
res = max(res, n-mid);
r=mid-1;
} else {
res = max(res, citations[mid]);
l = mid+1;
}
}
return max(res, min(citations[l], n-l));
}
};

优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int hIndex(vector<int>& citations) {
int n = citations.size();
int l = 0, r = n-1;
int res = 0;
while(l <= r) {
int mid = (r - l) / 2 + l;
if(n - mid <= citations[mid]) {
r=mid - 1;
} else {
l = mid+1;
}
}
return n-l;
}
};

274. H 指数

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int hIndex(vector<int>& citations) {
int arr[1001] = {0};
int max_cite = 0;
int min_cite = INT_MAX;
for(int n : citations) {
arr[n]++;
max_cite = max(max_cite, n);
min_cite = min(min_cite, n);
}
int sum = 0, res = 0;
for(int i = max_cite; i >= min_cite; i--) {
sum += arr[i];
res = max(res, min(sum, i));
}
return res;
}
};

2558. 从数量最多的堆取走礼物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
long long pickGifts(vector<int>& gifts, int k) {
priority_queue<int> gift_heap;
for(int n : gifts) {
gift_heap.push(n);
}
long long res = 0;
while(k--) {
gift_heap.push(sqrt(gift_heap.top()));
gift_heap.pop();
}
while(!gift_heap.empty()) {
res += gift_heap.top();
gift_heap.pop();
}
return res;
}
};

1465. 切割后面积最大的蛋糕

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
int maxArea(int h, int w, vector<int>& horizontalCuts, vector<int>& verticalCuts) {
int h_len = horizontalCuts.size();
int v_len = verticalCuts.size();
sort(verticalCuts.begin(), verticalCuts.end());
sort(horizontalCuts.begin(), horizontalCuts.end());
int max_h = max(h - horizontalCuts.back(), horizontalCuts.front());
int max_v = max(w - verticalCuts.back(), verticalCuts.front());
for(int i = 1; i < h_len; i++) {
max_h = max(max_h, horizontalCuts[i] - horizontalCuts[i-1]);
}
for(int i = 1; i < v_len; i++) {
max_v = max(max_v, verticalCuts[i] - verticalCuts[i-1]);
}
return ((long long)(max_h)%1000000007 * (max_v)%1000000007) % 1000000007;
}
};

2520. 统计能整除数字的位数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int countDigits(int num) {
int n = num;
int ret = 0;
while(n) {
if(num % (n % 10) == 0) {
ret++;
}
n /= 10;
}
return ret;
}
};

2698. 求一个整数的惩罚数

思路

  • 首先要计算出所有满足sum(split(i*i)) == i的元素
  • 发现很少,直接放到数组里去查表
  • 对于一个数i,希望它拆分后和等于target
    • 先分成a,b两份,判断是否等于,等于return true,不等于就把a分开(递归)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public:
int punishmentNumber(int n) {
int res = 0;
// for(int i = 1; i <= 1000; i++) {
// int n = i*i;
// if(search(i*i, i)) {
// cout << i << ", ";
// }
// }
int punish_arr[] = {1, 9, 10, 36, 45, 55, 82, 91, 99, 100, 235, 297, 369, 370, 379, 414, 657, 675, 703, 756, 792, 909, 918, 945, 964, 990, 991, 999, 1000};
int len = sizeof(punish_arr)/sizeof(int);
for(int i = 0; i < len; i++) {
if(n >= punish_arr[i]) res += punish_arr[i]*punish_arr[i];
}
return res;
}
bool search(int i, int target) {
int n = i;
int base = 1;
int r = 0;
while(n) {
r += base*(n%10);
if(r+n/10 == target) {
return true;
} else {
if(search(n/10, target - r)){
return true;
}
}
n /= 10;
base *= 10;
}
return false;
}
};

1155. 掷骰子等于目标和的方法数

思路

  • 先暴力递归,然后发现可以总结出dp公式,然后把递归改成dp尝试,成功了

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
vector<vector<int>> dp;
int numRollsToTarget(int n, int k, int target) {
dp = vector<vector<int>>(30+1, vector<int>(1000+1, 0));
dp[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int t = 1; t <= target; t++)
for(int K = 1;K <= k; K++)
if(t >= K)
dp[i][t] = (dp[i][t] + dp[i-1][t-K]) % 1000000007;
}
return dp[n][target];
}
int search(int n, int k, int target) {
if(target == 0 && n == 0) {
return 1;
} else if(n == 0) return 0;
int res = 0;
for(int i = 1; i <= k; i++) {
if(target >= i) {
res += numRollsToTarget(n-1, k, target-i);
}
}
return res;
}
};

优化

观察代码可知,dp[i][t]就是dp[i-1][t-1]+…dp[i-1][t-k],可以用前缀和优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
vector<vector<int>> dp;
int numRollsToTarget(int n, int k, int target) {
dp = vector<vector<int>>(30+1, vector<int>(1000+1,0));
dp[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int t = 1; t <= target; t++) {
dp[i][t] = (dp[i][t-1] + dp[i-1][t-1]) % 1000000007;
}
for(int t = target; t >= k; t--) {
dp[i][t] = (dp[i][t] - dp[i][t-k] + 1000000007) % 1000000007;
}
}
return dp[n][target];
}
};

1402. 做菜顺序

  • 一眼dp,鉴定为,纯纯的简单题
  • 一遍就做对啦哈哈哈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
int maxSatisfaction(vector<int>& satisfaction) {
int n = satisfaction.size();
vector<int> dp(n+1, 0);
vector<int> sums(n+1, 0);
sums[0] = accumulate(satisfaction.begin(), satisfaction.end(), 0);
sort(satisfaction.begin(), satisfaction.end(), less<int>());
for(int i = 0; i < n; i++) {
dp[0] += (i+1) * satisfaction[i];
}
for(int i = 1; i <= n; i++) {
if(dp[i-1] < dp[i-1] - sums[i-1]) {
dp[i] = dp[i-1] - sums[i-1];
sums[i] = sums[i-1] - satisfaction[i-1];
} else {
dp[i] = dp[i-1];
sums[i] = sums[i-1];
}
}
return dp[n];
}
};

优化

dp数组和sum数组可以优化掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
int maxSatisfaction(vector<int>& satisfaction) {
int n = satisfaction.size();
int dp = 0;
int sums = 0;
sort(satisfaction.begin(), satisfaction.end());
for(int i = 0; i < n; i++) {
dp += (i+1) * satisfaction[i];
sums += satisfaction[i];
}
for(int i = 1; i <= n; i++) {
if(dp < dp - sums) {
dp = dp - sums;
sums = sums - satisfaction[i-1];
}
}
return dp;
}
};

2678. 老人的数目

1
2
3
4
5
6
7
8
9
10
class Solution {
public:
int countSeniors(vector<string>& details) {
int res = 0;
for(const string &laodeng : details) {
if((laodeng[11] == '6' && laodeng[12] > '0') || (laodeng[11] > '6' && laodeng[12] >= '0')) res++;
}
return res;
}
};

2316. 统计无向图中无法互相到达点对数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
vector<vector<int>> g;
vector<bool> visited;
long long countPairs(int n, vector<vector<int>>& edges) {
g = move(vector<vector<int>>(n, vector<int>()));
visited = move(vector<bool>(n, false));
for(const vector<int>& edge : edges) {
g[edge[0]].push_back(edge[1]);
g[edge[1]].push_back(edge[0]);
}
long long res = 0;
for(int i = 0; i < n; i++) {
if(!visited[i]) {
long long node_num = count_nodes(i);
res += (n - node_num) * node_num;
}
}
return res >> 1;
}
int count_nodes(int root) {
visited[root] = true;
int res = 1;
for(int child : g[root]) {
if(!visited[child])
res += count_nodes(child);
}
return res;
}
};

并查集

  • 这里一直写不出来,发现并查集无法把所有相连的点归到一个集合中,因为在遍历的时候没有用union操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
vector<int> s;
int find(int x) {
return x == s[x] ? x : (s[x] = find(s[x]));
}
long long countPairs(int n, vector<vector<int>>& edges) {
s = move(vector<int>(n, 0));
vector<int> cnt = move(vector<int>(n, 0));
iota(s.begin(), s.end(), 0);
size_t elen = edges.size();
for(int j = 0; j < elen; j++) {
s[find(edges[j][1])] = find(edges[j][0]);
}
for(int i = 0; i < n; i++) {
cnt[find(i)]++;
}
long long res = 0;
for(int i = 0; i < n; i++) {
res += (n - (long long)cnt[i])*cnt[i];
}
return res >> 1;
}
};

继续优化

  • 换个公式减少一次循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
vector<int> s;
int find(int x) {
return x == s[x] ? x : (s[x] = find(s[x]));
}
long long countPairs(int n, vector<vector<int>>& edges) {
s = move(vector<int>(n, 0));
vector<int> cnt = move(vector<int>(n, 0));
iota(s.begin(), s.end(), 0);
size_t elen = edges.size();
for(int j = 0; j < elen; j++) {
s[find(edges[j][1])] = find(edges[j][0]);
}
long long res = 0;
for(int i = 0; i < n; i++) {
res += cnt[find(i)]++;
}
return (long long)n*(n-1)/2 - res;
}
};

2525. 根据规则将箱子分类

1
2
3
4
5
6
7
8
9
10
11
class Solution {
public:
string categorizeBox(int length, int width, int height, int mass) {
bool bulky = length >= 10000 || height >= 10000 || width >= 10000 || (long long)length*width*height >= 1000000000;
bool heavy = mass >= 100;
if(bulky && heavy) return "Both";
else if(!bulky && !heavy) return "Neither";
else if(bulky && !heavy) return "Bulky";
else return "Heavy";
}
};

117. 填充每个节点的下一个右侧节点指针 II

思路

  • 首先想到的就是层次遍历,非常简单
  • 对于进阶使用O(1)空间的算法,一直没想到利用已有的next指针
    • 树每层最左侧节点找parent,把同层都连起来就好
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Solution {
public:
Node* levelConnect(Node* root) {
queue<Node *> q;
if(root) q.push(root);
while(!q.empty()) {
Node *node = nullptr;
int n = q.size();
while(n--) {
if(node) node->next = q.front();
node = q.front();
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
q.pop();
}
}
return root;
}
Node* connect(Node* root) {
// return levelConnect(root);
if(root) recursiveConnect(root, new Node(0, root, nullptr, nullptr), true);
return root;
}
void recursiveConnect(Node* root, Node *parent, bool isLeft) {
Node *move = root;
while(move && !move->next) {
Node *to = nullptr;
if(isLeft && parent->right) {
to = parent->right;
isLeft = false;
} else {
Node *pMove = parent->next;
while(pMove && !pMove->left && !pMove->right) pMove = pMove->next;
if(pMove) {
if(pMove->left) {
isLeft = true;
to = pMove->left;
} else if(pMove->right) {
isLeft = false;
to = pMove->right;
}
parent = pMove;
}
}
move->next = to;
move = to;
}
if(root->right) recursiveConnect(root->right, root, false);
if(root->left) recursiveConnect(root->left, root, true);
}
};

整理下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Solution {
public:
Node* levelConnect(Node* root) {
queue<Node *> q;
if(root) q.push(root);
while(!q.empty()) {
Node *node = nullptr;
int n = q.size();
while(n--) {
if(node) node->next = q.front();
node = q.front();
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
q.pop();
}
}
return root;
}
Node* connect(Node* root) {
// return levelConnect(root);
if(root) recursiveConnect(root, new Node(0, root, nullptr, nullptr));
return root;
}
void recursiveConnect(Node* root, Node *parent) {
Node *move = root;
while(move && !move->next) {
Node *to = nullptr;
if(move != parent->left || !parent->right) {
parent = parent->next;
}
Node *pMove = parent;
while(pMove && !pMove->left && !pMove->right) pMove = pMove->next;
to = pMove
? (pMove->left == move
? pMove->right
: pMove->left
? pMove->left
: pMove->right)
: nullptr; // 你就猜吧,一猜一个不吱声
parent = pMove;
move->next = to;
move = to;
}
if(root->right) recursiveConnect(root->right, root);
if(root->left) recursiveConnect(root->left, root);
}
};

2127. 参加会议的最多员工数

并查集

  • 一个重要结论,图中一定有环(n个节点,大于n-1条边)
  • 如果环的大小大于2,则只能将环上的节点上桌,支链上的点无法上桌
  • 如果环的大小不大于2,则环上节点的最长支链也可以跟着上桌
  • 多个环长为2的子图(不论是否有支链),都可以全部上桌

故,统计所有环长大于2的最大环长,为maxRing
统计所有环长不大于2的环长之和,以及环上各节点的最大之链长度之和,为maxNumber
返回max(maxRing, maxNumber)

步骤

  • 并查集将节点分类
  • degree统计每个节点的入度
  • dSetSize统计每个集合的大小
  • 按照是否存在入度为0的点,将子图划分两类
    • 存在,则图含有支链,计算环的长度(findMaxRing)
      • 大于2,统计最大环长(maxRing)
      • 不大于2,从任意度为0的节点开始遍历,将环上节点的visited变成非0,并标记该子图为is2,累加maxNumber(环长为2,都可以上桌)
    • 不存在,则图不含有支链,计算环的长度(findMaxRing)
      • 大于2,统计最大环长(maxRing)
      • 不大于2,累加如maxNumber(环长为2,都可以上桌)
  • 对所有入度为0,且所在子图是is2的节点,计算该点到环上点的距离为支链长度,记录最大支链长度
  • 所有最大支链长度累加maxNumber
  • 返回max(maxNumber, maxRing)

时间 1848 ms 击败 5.2%
内存 98.4 MB 击败 35.8%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
class Solution {
public:
int maxRing = 0;
vector<int> visited;
vector<int> addtional_len;
vector<int> dSet;
vector<bool> dSetVisited;
vector<int> dSetSize;
vector<bool> isRing;
vector<bool> is2;
vector<int> degree;
int find(int i) {
return i == dSet[i] ? i : (dSet[i] = find(dSet[i]));
}
int maximumInvitations(vector<int>& favorite) {
int n = favorite.size();
visited = move(vector<int>(n, 0));
dSetVisited = move(vector<bool>(n, false));
isRing = move(vector<bool>(n, true));
is2 = move(vector<bool>(n, false));
addtional_len = move(vector<int>(n, 0));
degree = move(vector<int>(n, 0));
dSet = move(vector<int>(n, 0));
iota(dSet.begin(), dSet.end(), 0);
dSetSize = move(vector<int>(n, 0));
for(int i = 0; i < n; i++) {
degree[favorite[i]]++;
dSet[find(i)] = find(favorite[i]);
}
for(int i = 0; i < n; i++) {
dSetSize[find(i)]++;
if(degree[i] == 0) isRing[dSet[i]] = false;
}
int maxNumber = 0;
for(int i = 0; i < n; i++) {
int len = 1;
int ringNode = -1;
if(isRing[dSet[i]]) {
if(!dSetVisited[dSet[i]]) {
dSetVisited[dSet[i]] = true;
int ring = dSetSize[dSet[i]]++;
if(ring > 2) {
maxRing = max(maxRing, ring);
} else {
maxNumber += ring;
}
}
} else {
if(!dSetVisited[dSet[i]] && degree[i] == 0) {
dSetVisited[dSet[i]] = true;
int ring = findMaxRing(favorite, i, len, ringNode);
if(ring > 2) {
maxRing = max(maxRing, ring);
} else {
// addtional_len[ringNode] = max(len - ring, addtional_len[ringNode]);
int move = i;
while(move != ringNode) {
visited[move] = false;
move = favorite[move];
}
is2[dSet[i]] = true;
maxNumber += ring;
}
}
}
}
for(int i = 0; i < n; i++) {
if(is2[dSet[i]] && degree[i] == 0) {
int move = i;
int len = 0;
while(!visited[move]) {
len++;
move = favorite[move];
}
addtional_len[move] = max(addtional_len[move], len);
}
}
for(int i = 0; i < n; i++) {
// cout << i << ", " << addtional_len[i] << endl;
if(addtional_len[i] > 0) {
maxNumber += addtional_len[i];
}
}
return max(maxNumber, maxRing);
}

int findMaxRing(const vector<int>& favorite, int node, int &len, int &ringNode) {
visited[node] = len;
int child = favorite[node];
if(visited[child]) {
ringNode = child;
return len - visited[child] + 1;
}
len++;
return findMaxRing(favorite, child, len, ringNode);
}
};
  • 优化

上面的代码在遇到有公共部分的支链,(会分叉的支链时),会导致重叠部分反复被访问

使用arcFav统计被喜欢图,根据被喜欢图从环上开始寻找最大支链,可以避免,速度也得到了极大的优化

时间 284ms 击败 12.78% 使用 C++ 的用户
内存 166.84MB 击败 25.57% 使用 C++ 的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
class Solution {
public:
int maxRing = 0;
vector<int> visited;
vector<int> addtional_len;
vector<vector<int>> arcFav;
vector<int> dSet;
vector<bool> dSetVisited;
vector<int> dSetSize;
vector<bool> isRing;
vector<int> degree;
int find(int i) {
return i == dSet[i] ? i : (dSet[i] = find(dSet[i]));
}
int maximumInvitations(vector<int>& favorite) {
int n = favorite.size();
visited = move(vector<int>(n, 0));
arcFav = move(vector<vector<int>>(n, vector<int>()));
dSetVisited = move(vector<bool>(n, false));
isRing = move(vector<bool>(n, true));
addtional_len = move(vector<int>(n, 0));
degree = move(vector<int>(n, 0));
dSet = move(vector<int>(n, 0));
iota(dSet.begin(), dSet.end(), 0);
dSetSize = move(vector<int>(n, 0));
for(int i = 0; i < n; i++) {
degree[favorite[i]]++;
dSet[find(i)] = find(favorite[i]);
arcFav[favorite[i]].push_back(i);
}
for(int i = 0; i < n; i++) {
dSetSize[find(i)]++;
if(degree[i] == 0) isRing[dSet[i]] = false;
}
int maxNumber = 0;
for(int i = 0; i < n; i++) {
int len = 1;
int ringNode = -1;
if(isRing[dSet[i]]) {
if(!dSetVisited[dSet[i]]) {
dSetVisited[dSet[i]] = true;
int ring = dSetSize[dSet[i]]++;
if(ring > 2) {
maxRing = max(maxRing, ring);
} else {
maxNumber += ring;
}
}
} else {
if(!dSetVisited[dSet[i]] && degree[i] == 0) {
dSetVisited[dSet[i]] = true;
int ring = findMaxRing(favorite, i, len, ringNode);
if(ring > 2) {
maxRing = max(maxRing, ring);
} else {
int move = i;
while(move != ringNode) {
visited[move] = 0;
move = favorite[move];
}
addtional_len[ringNode] = searchArcFav(ringNode);
move = favorite[ringNode];
while(move != ringNode) {
addtional_len[move] = searchArcFav(move);
move = favorite[move];
}
maxNumber += ring;
}
}
}
}
for(int i = 0; i < n; i++) {
if(addtional_len[i] > 0) {
maxNumber += addtional_len[i];
}
}
return max(maxNumber, maxRing);
}
int searchArcFav(int move) {
visited[move] = 1;
int ret = 0;
for(int child : arcFav[move]) {
if(!visited[child]) {
ret = max(searchArcFav(child)+1, ret);
}
}
return ret;
}
int findMaxRing(const vector<int>& favorite, int node, int &len, int &ringNode) {
visited[node] = len;
int child = favorite[node];
if(visited[child]) {
ringNode = child;
return len - visited[child] + 1;
}
len++;
return findMaxRing(favorite, child, len, ringNode);
}
};

并查集+拓扑排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
class Solution {
public:
int maxRing = 0;
vector<int> visited;
vector<int> addtional_len;
vector<vector<int>> arcFav;
vector<int> dSet;
vector<int> degree;
int find(int i) {
return i == dSet[i] ? i : (dSet[i] = find(dSet[i]));
}
int maximumInvitations(vector<int>& favorite) {
int n = favorite.size();
visited = move(vector<int>(n, 0));
arcFav = move(vector<vector<int>>(n, vector<int>()));
addtional_len = move(vector<int>(n, 0));
degree = move(vector<int>(n, 0));
dSet = move(vector<int>(n, 0));
iota(dSet.begin(), dSet.end(), 0);
vector<int> ringNodes;
unordered_map<int, int> ringSize;
for(int i = 0; i < n; i++) {
degree[favorite[i]]++;
dSet[find(i)] = find(favorite[i]);
arcFav[favorite[i]].push_back(i);
}
queue<int> topologyQueue;
for(int i = 0; i < n; i++) {
if(degree[i] == 0) {
topologyQueue.push(i);
}
}
while(!topologyQueue.empty()) {
int front = topologyQueue.front();
topologyQueue.pop();
if((--degree[favorite[front]]) == 0) {
topologyQueue.push(favorite[front]);
}
}
unordered_set<int> ring2set;
for(int i = 0; i < n; i++) {
if(degree[i] > 0) {
ringNodes.push_back(i);
ringSize[find(i)]++;
}
}
for(int i = 0; i < n; i++) {
if(ringSize[find(i)] <= 2) {
ring2set.insert(find(i));
}
}
int maxNumber = 0;
for(int ringNode : ringNodes) {
int ring = ringSize[dSet[ringNode]];
if(ring > 2) {
maxRing = max(maxRing, ring);
} else {
addtional_len[ringNode] = searchArcFav(ringNode);
}
}
for(int i = 0; i < n; i++) {
if(addtional_len[i] > 0) {
maxNumber += addtional_len[i]; // add ring number
}
}
return max(maxNumber + (ring2set.size() << 1), (size_t)maxRing);
}
int searchArcFav(int move) {
visited[move] = 1; // 判断, not in ring
int ret = 0;
for(int child : arcFav[move]) {
if(!visited[child] && degree[child] == 0) {
ret = max(searchArcFav(child)+1, ret);
}
}
return ret;
}
};

更慢了,原因是不能在拓扑排序的同时计算最大支链长度

优化掉并查集

  • 在拓扑排序中寻找最长支链,为每个节点定义f(x)
  • $ f(x) = max_{i=0}^j(f(aFav(x)_i) + 1) $
  • 每个节点初始为0,拓扑排序时,每次i将j的degree被减小,其值为max(f(i)+1, f(j))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Solution {
public:
int maxRing = 0;
vector<bool> visited;
vector<int> degree;
vector<int> f;
int maximumInvitations(vector<int>& favorite) {
int n = favorite.size();
visited = move(vector<bool>(n, false));
degree = move(vector<int>(n, 0));
f = move(vector<int>(n, 0));
vector<int> ringNodes;
unordered_map<int, int> ringSize;
for(int i = 0; i < n; i++) {
degree[favorite[i]]++;
}
queue<int> topologyQueue;
for(int i = 0; i < n; i++) {
if(degree[i] == 0) {
topologyQueue.push(i);
}
}
while(!topologyQueue.empty()) {
int front = topologyQueue.front();
topologyQueue.pop();
f[favorite[front]] = max(f[favorite[front]], f[front] + 1);
visited[front] = true;
if((--degree[favorite[front]]) == 0) {
topologyQueue.push(favorite[front]);
}
}
int maxNumber = 0;
for(int i = 0; i < n; i++) {
if(!visited[i])
if(favorite[favorite[i]] == i) {
maxNumber += f[i] + 1;
} else {
int ring = 1;
int move = favorite[i];
while(!visited[move] && move != i) {
visited[move] = true;
move = favorite[move];
ring++;
}
maxRing = max(maxRing, ring);
}
}
return max(maxNumber, maxRing);
}
};
作者

Meow Meow Liu

发布于

2023-10-28

更新于

2024-04-23

许可协议

评论