leetcode 155 周賽
- 第一題: 排個序就好了。
- 第二題: 二分判斷前面的醜數的個數, 醜數個數的計算方式需要小心點。
- 第三題: 並茶集判斷聯通分量。
- 第四題: 拓撲排序, 對同一分組的添加源點和匯點。再進行多次拓撲。
5197. 最小絕對差
當a與b接近時, | a - b | 才儘可能小。直接排序完畢後, 相鄰兩個值相減得到值的絕對值充當map
的key
, 記錄一下這兩個兩個數即可。map的第一個元素就是最小絕對值差能對應的二元組列表。
class Solution {
public:
vector<vector<int>> minimumAbsDifference(vector<int>& arr)
{
map<int, vector<vector<int>>> ans;
sort(arr.begin(), arr.end());
for(int i = 1; i < arr.size(); ++i)
{
int sub = abs(arr[i] - arr[i - 1]);
ans[sub].push_back({arr[i - 1], arr[i]});
}
if(ans.empty()) return vector<vector<int>>();
else return ans.begin()->second;
}
};
5198. 醜數 III
二分法, 範圍爲[1, 2e9], 每次判斷mid
前面的醜數數量是否等於n
即可, 小於n
則l = mid + 1
, 大於 n
則 r = mid
, 即使 等於n n
, 實際的醜數也可能在mid
的前面一點點。
關鍵問題是怎麼判斷一個整數K, 其前面的醜數數量是多少, 判斷方式如下:
S(K, n) 表示k前面能被n整除的數數量
LCM(x, y) 表示 x 與 y的最小公倍數。
LCM(x, y, z) 表示x與y與z的最小公倍數。
k前面的醜數數量 = S(K, a) + S(K, b) + S(K, c) - S(K, LCM(a, b)) - S(K, LCM(a, c)) - S(K, LCM(b, c)) + S(K, LCM(a, b, c);)
class Solution {
public:
typedef long long ll;
ll gcd(ll a, ll b){return a % b == 0 ? b : gcd(b, a % b);}
ll lcm(ll a, ll b){return a * b / gcd(a, b);}
// 計算前面的數字有多少.
int preNum(ll n, ll a, ll b, ll c)
{
int sum = 0;
ll lab = lcm(a, b), lbc = lcm(b, c), lac = lcm(a,c),labc = lcm(lab, c);
sum = n / a + n / b - n / lab + n / c - n / lac - n / lbc + n / labc;
return sum;
}
int nthUglyNumber(int n, int a, int b, int c)
{
int l = 0, r = 2 * 1e9, mid = -1;
// [l, r)
while(l < r)
{
mid = l + (r - l) / 2;
int num = preNum(mid + 1, a, b, c);
if(num == n) break;
else if(num < n) l = mid + 1;
else r = mid;
}
mid += 1;
while(!(mid % a == 0 || mid % b == 0 || mid % c == 0))
mid -= 1;
return mid;
}
};
5199. 交換字符串中的元素
在一個聯通分量當中的字符可以得到當前聯通分量當中的最小字典序的子序列, 比如:
s = "dcab", pairs = [[0,3],[1,2]]
[0, 3] 在一個聯通分量當中, 故能得到最小字典序的序列 : bd
[1, 2] 在一個聯通分量當中, 故能得到最小字典序的序列 : ac
故組合起來爲 bacd
s = "dcab", pairs = [[0,3],[1,2],[0,2]]
[0, 1, 2, 3] 在一個聯通分量當中, 故能得到最小字典序列爲: abcd
故結果爲 abcd
使用並查集得到多個聯通分量, 分別求得這些聯通分量上面最小字典序, 後再組合起來即可。
class Solution {
public:
int uf[100000];
int ufind(int x) {return x == uf[x] ? x : uf[x] = ufind(uf[x]);}
string smallestStringWithSwaps(string s, vector<vector<int>>& pairs)
{
map<int, vector<int>> group; // key爲聯通分量的代表頂點, value爲聯通分量當中的頂點
for(int i = 0; i != s.size(); ++i) uf[i] = i; // 初始化聯通分量
for(int i = 0; i != pairs.size(); ++i)
{
int x = ufind(pairs[i][0]), y = ufind(pairs[i][1]);
if(x != y) uf[x] = uf[y];
}
// 劃分集合
for(int i = 0; i != s.size(); ++i)
{
int x = ufind(i);
group[x].push_back(i);
}
// 針對每個集合都有一個最小串
for(auto p : group)
{
vector<char> t;
vector<int> &ins = p.second;
if(ins.size() == 1) continue;
for(int i = 0; i != ins.size(); ++i)
t.push_back(s[ins[i]]);
sort(t.begin(), t.end());
for(int i = 0; i != t.size(); ++i)
{
s[ins[i]] = t[i];
}
}
return s;
}
};
5200. 項目管理
應該有更好的方案, 再研究研究…
- 方案1: 增加匯點和源點即可。
對一個分組當中的頂點添加一個源點和匯點, 指向當前分組當中的邊修改爲指向其源點, 該分組當中的頂點指向其他分組的頂點的邊的出發點修改爲其匯點, 沒有分組的頂點自己爲一個分組, 且源點和匯點爲其自身即可。對改造完畢的圖中的入度爲0的頂點依次進行拓撲排序即可。
- case:
8
2
[-1,-1,1,0,0,1,0,-1]
[[],[6],[5],[6],[3,6],[],[],[]]
- case1: 原圖
[外鏈圖片轉存失敗(img-flHiqnPd-1569242780713)
- case1: 增加源點和匯點後
紫線是拓撲排序的順序。
- 代碼實現:
class Solution {
public:
map<int, pair<int, set<int>>> mmp; // 拓撲排序的結構. 使用set是爲了去重方便。
map<int, pair<int, int>> ses; // first爲源點, second 爲匯點.
set<pair<int, int>> edges; // 額外添加的邊
// 尋找編號爲i的頂點的源點和匯點, 找到的話如果沒有構造源點, 匯點與該點的聯繫的話則構造一次;
// 沒有分組的頂點 源點和 匯點爲其自身。
pair<int, int> findBE(int i, vector<int> &group)
{
int x = -1, y = -1;
if(ses.find(group[i]) != ses.end()) x = ses[group[i]].first, y = ses[group[i]].second;
if(x == -1 && y == -1) x = y = i;
//添加當前頂點和源點和匯點之間的關係, 已經建立過則不要再進行建立
if((x != y) && (mmp[x].second.find(i) == mmp[x].second.end()))
{
mmp[x].second.insert(i);
mmp[i].first += 1;
mmp[i].second.insert(y);
mmp[y].first += 1;
}
return {x, y};
}
vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& bes)
{
mmp.clear();
ses.clear();
edges.clear();
//給每個分組分配源點和匯點
int index = n;
for(int i = 0; i != group.size(); ++i)
{
if(group[i] >= 0 && ses.find(group[i]) == ses.end())
{
ses[group[i]] = make_pair(index, index + 1);
index += 2;
}
}
// 構建鄰接表並添加匯點和源點。
for(int i = 0; i != bes.size(); ++i)
{
// 在圖中鄰接表當中
if(mmp.find(i) == mmp.end())
mmp[i] = make_pair(0, set<int>());
// 找到i的源點和匯點
pair<int, int> bei = findBE(i, group);
for(auto j : bes[i])
{
pair<int, int> bej = findBE(j, group);
// 源點和匯點相同可以直接添加:
if(bei.first == bej.first)
{
mmp[j].second.insert(i);
mmp[i].first += 1;
}
// 不同則匯點指向源點即可, 需要進行去重
else
{
if(mmp[bej.second].second.find(bei.first) == mmp[bej.second].second.end())
{
mmp[bej.second].second.insert(bei.first);
mmp[bei.first].first += 1;
}
}
}
}
// 拓撲排序
vector<int> ans;
queue<int> Q;
vector<int> sources;
// 一定要保存最初狀態的入度爲0的頂點。
for(auto p : mmp)
{
if(p.second.first == 0)
{
sources.push_back(p.first);
}
}
for(auto p : sources)
{
Q.push(p);
while(!Q.empty())
{
int node = Q.front(); Q.pop();
// 將node鄰接到的頂點的入度減少1
for(auto to : mmp[node].second)
{
mmp[to].first -= 1;
if(mmp[to].first == 0)
Q.push(to);
}
if(node < n)
ans.push_back(node);
}
}
if(ans.size() < n ) return vector<int>();
return ans;
}
};