LeetCode 周賽223
技術標籤:# leetcode
考完試,康復訓練一下
5649. 解碼異或後的陣列
未知 整數陣列 arr 由 n 個非負整陣列成。
經編碼後變為長度為 n - 1 的另一個整數陣列 encoded ,其中 encoded[i] = arr[i] XOR arr[i + 1] 。例如,arr = [1,0,2,1] 經編碼後得到 encoded = [1,2,3] 。
給你編碼後的陣列 encoded 和原陣列 arr 的第一個元素 first(arr[0])。
請解碼返回原陣列 arr 。可以證明答案存在並且是唯一的。
示例 1:
輸入:encoded = [1,2,3], first = 1
解釋:若 arr = [1,0,2,1] ,那麼 first = 1 且 encoded = [1 XOR 0, 0 XOR 2, 2 XOR 1] = [1,2,3]
示例 2:
輸入:encoded = [6,2,7,3], first = 4
輸出:[4,2,0,7,4]
提示:
2 <= n <= 104
encoded.length == n - 1
0 <= encoded[i] <= 105
0 <= first <= 105
思路:
因為
x
⊕
x
=
0
x ⊕ x=0
x⊕x=0,所以
a
[
i
]
⊕
e
n
c
o
d
e
d
[
i
]
=
a
[
i
+
1
]
a[i]⊕encoded[i]=a[i+1]
class Solution {
public:
vector<int> decode(vector<int>& encoded, int first) {
vector<int>ans;
ans.push_back(first);
for(int i = 0;i < encoded.size();i++) {
ans.push_back(ans.back() ^ encoded[i]);
}
return ans;
}
};
5652. 交換連結串列中的節點
給你連結串列的頭節點 head 和一個整數 k 。
交換 連結串列正數第 k 個節點和倒數第 k 個節點的值後,返回連結串列的頭節點(連結串列 從 1 開始索引)。
示例 1:
輸入:head = [1,2,3,4,5], k = 2
輸出:[1,4,3,2,5]
示例 2:
輸入:head = [7,9,6,6,7,8,3,0,9,5], k = 5
輸出:[7,9,6,6,8,7,3,0,9,5]
示例 3:
輸入:head = [1], k = 1
輸出:[1]
示例 4:
輸入:head = [1,2], k = 1
輸出:[2,1]
示例 5:
輸入:head = [1,2,3], k = 2
輸出:[1,2,3]
思路:
對連結串列不是很熟悉,所以用的很暴力的做法,就是把連結串列拆到數組裡面,然後找到第k個數和倒數第k個數,再遍歷一次連結串列交換。(連結串列沒有前向指標,所以也沒想到什麼優雅的做法)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapNodes(ListNode* head, int k) {
vector<int>num;
ListNode*h1 = head;
while(h1 != NULL) {
num.push_back(h1->val);
h1 = h1->next;
}
int n = num.size();
ListNode*h2 = head;
int cnt = 0;
while(h2 != NULL) {
if(cnt == k - 1) {
h2 -> val = num[n - k];
} else if(cnt == n - k) {
h2 -> val = num[k - 1];
}
cnt++;
h2 = h2->next;
}
return head;
}
};
5650. 執行交換操作後的最小漢明距離
題目難度Medium
給你兩個整數陣列 source 和 target ,長度都是 n 。還有一個數組 allowedSwaps ,其中每個 allowedSwaps[i] = [ai, bi] 表示你可以交換陣列 source 中下標為 ai 和 bi(下標從 0 開始)的兩個元素。注意,你可以按 任意 順序 多次 交換一對特定下標指向的元素。
相同長度的兩個陣列 source 和 target 間的 漢明距離 是元素不同的下標數量。形式上,其值等於滿足 source[i] != target[i] (下標從 0 開始)的下標 i(0 <= i <= n-1)的數量。
在對陣列 source 執行 任意 數量的交換操作後,返回 source 和 target 間的 最小漢明距離 。
示例 1:
輸入:source = [1,2,3,4], target = [2,1,4,5], allowedSwaps = [[0,1],[2,3]]
輸出:1
解釋:source 可以按下述方式轉換:
- 交換下標 0 和 1 指向的元素:source = [2,1,3,4]
- 交換下標 2 和 3 指向的元素:source = [2,1,4,3]
source 和 target 間的漢明距離是 1 ,二者有 1 處元素不同,在下標 3 。
示例 2:
輸入:source = [1,2,3,4], target = [1,3,2,4], allowedSwaps = []
輸出:2
解釋:不能對 source 執行交換操作。
source 和 target 間的漢明距離是 2 ,二者有 2 處元素不同,在下標 1 和下標 2 。
示例 3:
輸入:source = [5,1,2,4,3], target = [1,5,4,2,3], allowedSwaps = [[0,4],[4,2],[1,3],[1,4]]
輸出:0
提示:
n == source.length == target.length
1 <= n <= 105
1 <= source[i], target[i] <= 105
0 <= allowedSwaps.length <= 105
allowedSwaps[i].length == 2
0 <= ai, bi <= n - 1
ai != bi
思路:
能交換的下標部分用並查集連起來,然後用一個
m
a
p
map
map標記這個並查集裡面有哪些數。遍歷
t
a
r
g
e
t
target
target陣列,看
t
a
r
g
e
t
[
i
]
target[i]
target[i]對於下標
i
i
i的並查集中,是否存在
t
a
r
g
e
t
[
i
]
target[i]
target[i]這個數;如果存在,那麼直接換過來(具體過程咱不考慮),然後
m
p
[
r
o
o
t
[
i
]
]
[
t
a
r
g
e
t
[
i
]
]
−
−
mp[root[i]][target[i]]--
mp[root[i]][target[i]]−−。否則就說明
t
a
r
g
e
t
[
i
]
target[i]
target[i]一定不等於
s
o
u
r
c
e
[
i
]
source[i]
source[i],那麼海明距離
a
n
s
+
+
ans++
ans++。
class Solution {
public:
int fa[100005];
map<int,int>mp[100005];
int findset(int x) {
if(x == fa[x]) return x;
return fa[x] = findset(fa[x]);
}
void Union(int x,int y) {
int fx = findset(x),fy = findset(y);
if(fx != fy) fa[fx] = fy;
}
int minimumHammingDistance(vector<int>& source, vector<int>& target, vector<vector<int>>& allowedSwaps) {
int n = source.size();
for(int i = 0;i < n;i++) {
fa[i] = i;
mp[i].clear();
}
for(int i = 0;i < allowedSwaps.size();i++) {
int x = allowedSwaps[i][0],y = allowedSwaps[i][1];
Union(x,y);
}
for(int i = 0;i < n;i++) {
int x = findset(i);
mp[x][source[i]]++;
}
int ans = 0;
for(int i = 0;i < n;i++) {
int x = findset(i);
if(mp[x][target[i]]) {
mp[x][target[i]]--;
} else {
ans++;
}
}
return ans;
}
};
5639. 完成所有工作的最短時間
題目難度Hard
給你一個整數陣列 jobs ,其中 jobs[i] 是完成第 i 項工作要花費的時間。
請你將這些工作分配給 k 位工人。所有工作都應該分配給工人,且每項工作只能分配給一位工人。工人的 工作時間 是完成分配給他們的所有工作花費時間的總和。請你設計一套最佳的工作分配方案,使工人的 最大工作時間 得以 最小化 。
返回分配方案中儘可能 最小 的 最大工作時間 。
示例 1:
輸入:jobs = [3,2,3], k = 3
輸出:3
解釋:給每位工人分配一項工作,最大工作時間是 3 。
示例 2:
輸入:jobs = [1,2,4,7,8], k = 2
輸出:11
解釋:按下述方式分配工作:
1 號工人:1、2、8(工作時間 = 1 + 2 + 8 = 11)
2 號工人:4、7(工作時間 = 4 + 7 = 11)
最大工作時間是 11 。
提示:
1 <= k <= jobs.length <= 12
1 <= jobs[i] <= 107
思路:
- 一看這範圍:12,不是搜尋就是狀壓,衝啊。
- 一算複雜度,爆搜是 n k n^k nk,如果k≤5,那麼 5 12 = 244140625 5^{12}=244140625 512=244140625,再剪剪枝還是可以接受的。但是 k k k再往上按照爆搜的寫法就肯定不行的了,咋優化?
- 一個很明顯的事實就是, k ≥ n + 1 2 k≥\frac{n+1}{2} k≥2n+1的時候,最大的結果不可能超過 j o b s jobs jobs陣列兩兩匹配的結果。而要使最大值最小,我們肯定不會讓 j o b s jobs jobs陣列最大的 k k k個數兩兩匹配,所以一開始就將最大的 k k k個數分給不同的工人。此時 n = n − k n=n-k n=n−k,複雜度此時最大 5 12 5^{12} 512,這就可以爆搜寫嘍。
class Solution {
public:
vector<int>job;
int num[20],ans;//每個人的時間量
int n;
void dfs(int now,int k) {
if(now == n) {
int tmp = 0;
for(int i = 0;i < k;i++) {
tmp = max(tmp,num[i]);
}
ans = min(ans,tmp);
return;
}
for(int i = 0;i < k;i++) {
if(num[i] + job[now] < ans) {
num[i] += job[now];
dfs(now + 1,k);
num[i] -= job[now];
}
}
}
int minimumTimeRequired(vector<int>& jobs, int k) {
n = jobs.size();
sort(jobs.begin(),jobs.end());
memset(num,0,sizeof(num));
job = jobs;
ans = 1e9;
if(k >= (n + 1) / 2) {
for(int i = 0;i < k;i++) {
num[i] += jobs[i + n - k];
}
n -= k;
}
dfs(0,k);
return ans;
}
};