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;
}
};