首页 > 代码库 > Codility上的练习 (13)
Codility上的练习 (13)
(1)AbsDistinct
给定一个按非递减顺序排好顺序的非空整数数组,问里面右多少种不同的绝对值。
数据范围:整数数组长度[1..10^5], 整数范围[-2147483648, +2147483647]。
要求复杂度 : 时间O(N),空间O(1)
分析: 题目不难…… 但是细节很重要。因为整数直接取绝对值可能回溢出(例如-2147483648),而且我们没有额外空间hash。所以一个好办法是类似合并两个有序序列。我们从最小的负数和最大的正数开始类似归并排序那么做。这样,正负数都是按照绝对值逐渐减小的顺序遍历的。我们把正数和负数的绝对值想像成两个递减的序列,然后按归并排序思路,每次大的动一下就可以了,直到一个列表为空的时候,我们需要把另外一个列表计算进去。要点就是可以用x + y的符号来代替绝对值比较,因为一个正数 + 负数 不会溢出。。。。
代码:
// you can use includes, for example: // #include <algorithm> // you can write to stdout for debugging purposes, e.g. // cout << "this is a debug message" << endl; int solution(vector<int> &A) { // write your code in C++11 int answer = 0; for (int i = 0, j = A.size() - 1;i <= j;) { if ((A[i] >= 0) || (A[j] <= 0)) { for (;i <= j;++answer) { for (int k = i; (i <= j) && (A[i] == A[k]); ++i) ; } } else { ++answer; int temp = A[i] + A[j]; if (temp < 0) { for (int k = i; (i <= j) && (A[i] == A[k]); ++i) ; } else if (temp > 0) { for (int k = j; (i <= j) && (A[j] == A[k]); --j) ; } else { for (int k = i; (i <= j) && (A[i] == A[k]); ++i) ; for (int k = j; (i <= j) && (A[j] == A[k]); --j) ; } } } return answer; }
(2) CountTriangles
给定正整数数组A,长度为N,下标从0开始,求(P,Q,R),满足0<=P<Q<R<N 并且 A[P] + A[Q] > A[R], A[Q] + A[R] > A[P], A[P] + A[R] > A[Q]的三元组个数。
数据范围 N [0..1000], 数组元素[1..10^9]。
要求复杂度 时间O(N ^ 2) ,空间 O(1)。
分析: 显然我们不能枚举……我们可以把数组排序 O(NlogN),甚至O(N^2)的排序都可以。然后还是枚举,只不过枚举两条较小的边A[x] , A[y], 然后我们考虑最大边A[z],设想假设我们固定x, 当y变大时A[x] + A[y]也变大,我们需要A[x] + A[y] > A[z], y变大之前的那些z值现在依然也满足条件,所以我们只要接着上次满足条件的最大的z,继续循环就可以了。所以对于同一个x来说,y和z的变化都是O(N)的。总复杂度O(N^2)。
代码:
// you can also use includes, for example: // #include <algorithm> #include <algorithm> int solution(vector<int> &A) { // write your code in C++98 sort(A.begin(), A.end()); int n = A.size(), result = 0; for (int x = 0; x < n; ++x) { int z = 0; for (int y = x + 1; y < n; ++y) { for (z = max(z, y + 1); (z < n) && (A[x] + A[y] > A[z]); ++z) ; result += z - y - 1; } } return result; }
数据范围: M [0..10^5],数组长度N [1..10^5]
要求复杂度 时间O(N), 空间O(M)。
分析: 这个问题很像只包含一种字符的子串,不同的是我们求的是个数。我们用O(M)的空间维护一个滑动窗口,里面的值是不重复的。对每个i,我们求出j满足窗口[i..j]之间元素是不重复的,则以i开头包含的不重复的子数组数显然是j - i + 1,然后我们滑动i + 1的时候,[i + 1..j]显然也没有重复字符,所以j不用重新算,接着算就好了。
代码:
// you can also use includes, for example: // #include <algorithm> int solution(int M, vector<int> &A) { // write your code in C++98 int j = 0, result = 0; vector<bool> have; have.resize(M + 1, false); for (int i = 0; i < A.size(); ++i) { for (; (j < A.size()) && (!have[A[j]]); ++j) { have[A[j]] = true; } if ((result += j - i) >= 1000000000) { return 1000000000; } have[A[i]] = false; } return result; }
(4) 给定一个整数数组,求两个数的和(可以是同一个数),满足绝对值最小。返回这个绝对值。
数据范围: 数组长度[1..10^5],数组元素[-10^9, +10^9]。
要求复杂度: 时间 O(NlogN), 空间O(1)。
分析: 和(1)差不多,先排序,再两边扫,类似2-sum,绝对值较大的指针动一下。
代码:
// you can also use includes, for example: // #include <algorithm> #include <algorithm> int ab(int x) { return (x >= 0)?x:(-x); } int f(int x,int y) { return ab(x + y); } int solution(vector<int> &A) { // write your code in C++98 vector<int> a = A; sort(a.begin(),a.end()); int answer = f(a[0],a[0]); for (int i = 0, j = a.size() - 1; i <= j; ) { answer = min(answer, f(a[i], a[j])); if (ab(a[i]) > ab(a[j])) { ++i; } else { --j; } } return answer; }
Codility上的练习 (13)