第一次玩这个,听说比周赛简单点,这次一共四道题,发现自己图论是真不会。
后面两题,第三题是裸的最小生成树,第四题是裸的拓扑排序。
- 最大唯一数 显示英文描述
用户通过次数 302
用户尝试次数 313
通过次数 307
提交次数 525
题目难度 Easy
给你一个整数数组 A,请找出并返回在该数组中仅出现一次的最大整数。
如果不存在这个只出现一次的整数,则返回 -1。
示例 1:
输入:[5,7,3,9,4,9,8,3,1]
输出:8
解释:
数组中最大的整数是 9,但它在数组中重复出现了。而第二大的整数是 8,它只出现了一次,所以答案是 8。
示例 2:
输入:[9,9,8,8]
输出:-1
解释:
数组中不存在仅出现一次的整数。
提示:
1 <= A.length <= 2000
0 <= A[i] <= 1000
分析:
这题有两种思路比较常见,我已开始用排序做…结果错了好几次,后面干脆用hash表读一下算了。
解法一:
排序后遍历
class Solution {
public:
int largestUniqueNumber(vector<int>& A) {
sort(A.begin(), A.end());
if(A.size() == 1) {
return A[0];
}
for(int i = A.size()-1; i >= 0; i--) {
if(i < A.size() -1 && A[i] == A[i+1]) {
continue;
}
if(i == 0 || A[i] != A[i-1]) {
return A[i];
}
}
return -1;
}
};
解法二:
hash
class Solution {
public:
int largestUniqueNumber(vector<int>& A) {
unordered_map<int, int> map;
for(int i = 0; i < A.size(); i++) {
map[A[i]]++;
}
for(int i = 1000; i >=0; i--) {
if(map[i] == 1) {
return i;
}
}
return -1;
}
};
- 阿姆斯特朗数 显示英文描述
用户通过次数 301
用户尝试次数 304
通过次数 306
提交次数 388
题目难度 Easy
假设存在一个 k 位数 N,其每一位上的数字的 k 次幂的总和也是 N,那么这个数是阿姆斯特朗数。
给你一个正整数 N,让你来判定他是否是阿姆斯特朗数,是则返回 true,不是则返回 false。
示例 1:
输入:153
输出:true
示例:
153 是一个 3 位数,且 153 = 1^3 + 5^3 + 3^3。
示例 2:
输入:123
输出:false
解释:
123 是一个 3 位数,且 123 != 1^3 + 2^3 + 3^3 = 36。
提示:
1 <= N <= 10^8
class Solution {
public:
bool isArmstrong(int N) {
int num = 0;
int temp1 = N;
while(N) {
N = N / 10;
num++;
}
N = temp1;
int temp = 0, sum = 0;
while(N) {
temp = N % 10;
sum += pow(temp, num);
N = N / 10;
}
return sum == temp1;
}
};
- 最低成本联通所有城市 显示英文描述
用户通过次数 79
用户尝试次数 148
通过次数 87
提交次数 535
题目难度 Medium
想象一下你是个城市基建规划者,地图上有 N 座城市,它们按以 1 到 N 的次序编号。
给你一些可连接的选项 conections,其中每个选项 conections[i] = [city1, city2, cost] 表示将城市 city1 和城市 city2 连接所要的成本。(连接是双向的,也就是说城市 city1 和城市 city2 相连也同样意味着城市 city2 和城市 city1 相连)。
返回使得每对城市间都存在将它们连接在一起的连通路径(可能长度为 1 的)最小成本。该最小成本应该是所用全部连接代价的综合。如果根据已知条件无法完成该项任务,则请你返回 -1。
示例 1:
输入:N = 3, conections = [[1,2,5],[1,3,6],[2,3,1]]
输出:6
解释:
选出任意 2 条边都可以连接所有城市,我们从中选取成本最小的 2 条。
示例 2:
输入:N = 4, conections = [[1,2,3],[3,4,4]]
输出:-1
解释:
即使连通所有的边,也无法连接所有城市。
提示:
1 <= N <= 10000
1 <= conections.length <= 10000
1 <= conections[i][0], conections[i][1] <= N
0 <= conections[i][2] <= 10^5
conections[i][0] != conections[i][1]
分析:
明确一下方向,求最小生成树一共有两种算法:prim算法和Kruskal算法。
prim算法适合稠密图,时间复杂度为O(n^2),n为结点数
Kruskal算法适合稀疏图,时间复杂度为O(eloge),e为边数
上面的题目属于稀疏图,选择Kruskal算法。
//并查集可以通过查询两个结点所在集合的根结点是否相同来判断他们是否在同一个集合
//而只要把测试边的两个端点所在集合合并,就能达到将边加入最小生成树的效果
class UnionSet{
public:
vector<int> father;
UnionSet(int n) {
father.resize(n+1);
for(int i = 1; i <= n; i++) {
father[i] = i;
}
}
int find(int x) {
while(father[x] != x) {
x = father[x];
}
return x;
}
void insert(int x, int y) {
father[find(x)] = y;
}
// int count() {
// int cnt = 0;
// for(int i = 1; i <= father.size(); i++) {
// if(father[i] == i) {
// cnt++;
// }
// }
// return cnt;
// }
};
class Solution {
public:
//const static int maxv = 10002;
static bool cmp(vector<int>& e1, vector<int>& e2) {
return e1[2] < e2[2];
}
int minimumCost(int N, vector<vector<int>>& conections) {
UnionSet un = UnionSet(N);
//对所有边按边权从小到大排序
sort(conections.begin(), conections.end(), cmp);
int sum = 0;
int cnt = 0;
for(int i = 0; i < conections.size(); i++) {
//按边权从小到大测试所有边,如果当前测试边所连接的两个顶点不在同一个连通块中,则把这条测试边加入到当前最小生成树中;否则,将边舍弃
if(un.find(conections[i][0]) != un.find(conections[i][1])) {
un.insert(conections[i][0], conections[i][1]);
cnt++;
sum += conections[i][2];
}
}
//直到最小生成树中的边数等于总顶点数减1或是测试完所有边时结束
if(cnt == N - 1) {
return sum;
} else {
return -1;
}
}
};
- 平行课程 显示英文描述
用户通过次数 101
用户尝试次数 127
通过次数 104
提交次数 221
题目难度 Hard
已知有 N 门课程,它们以 1 到 N 进行编号。
给你一份课程关系表 relations[i] = [X, Y],用以表示课程 X 和课程 Y 之间的先修关系:课程 X 必须在课程 Y 之前修完。
假设在一个学期里,你可以学习任何数量的课程,但前提是你已经学习了将要学习的这些课程的所有先修课程。
请你返回学完全部课程所需的最少学期数。
如果没有办法做到学完全部这些课程的话,就返回 -1。
示例 1:
输入:N = 3, relations = [[1,3],[2,3]]
输出:2
解释:
在第一个学期学习课程 1 和 2,在第二个学期学习课程 3。
示例 2:
输入:N = 3, relations = [[1,2],[2,3],[3,1]]
输出:-1
解释:
没有课程可以学习,因为它们相互依赖。
提示:
1 <= N <= 5000
1 <= relations.length <= 5000
relations[i][0] != relations[i][1]
输入中没有重复的关系
思路:
求解拓扑序列:
- 定义一个队列q,并把所有入度为0的结点加入队列
- 取队首结点,输出。然后删去所有从它出发的边,并令这些边到达的顶点的入度减1,如果某个顶点的入度减为0,则将其加入队列。
- 反复进行2操作,直到队列为空。如果队列为空时入队的结点数目恰好为n,说明拓扑排序成功,图G为有向无环图;
否则,拓扑排序失败,图G中有环。
class Solution {
private:
int const static maxv = 5001;
public:
int minimumSemesters(int N, vector<vector<int>>& relations) {
int num = 0;
queue<int> q;
int inD[maxv];
vector<int> edge[maxv];
memset(inD, 0, sizeof(inD));
for(int i = 0; i < relations.size(); i++) {
inD[relations[i][1]]++;
edge[relations[i][0]].push_back(relations[i][1]);
}
int b[maxv];
int res = 0;
memset(b, 0, sizeof(b));
for(int i = 1; i <= N; i++) {
if(inD[i] == 0) {
q.push(i);
b[i] = 1;
res = 1;
num++;
}
}
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = 0; i < edge[u].size(); i++) {
int v = edge[u][i];
inD[v]--;
if(inD[v] == 0) {
q.push(v);
b[v] = b[u] + 1;
res = b[u] + 1;
num++;
}
}
}
if(num == N) {
return res;
} else {
return -1;
}
}
};