这个问题现在还在比赛中……不过8月底应该就结束了。题目不难,是说有N个时钟,每个时钟有M个指针,有P个刻度。时钟是圆形的,P个刻度均分整个圆。每个时钟每个指针指向整数刻度,并且每个时钟自身指针指向的数字都不同。你可以任意旋转时钟的表盘,但是你不能转指针。问最后有多少对时钟可以变成相同的状态。(状态是指每个指针的相对位置一样)。例如:
原始这个样子的时钟,可以旋转成
忽略刻度。然后(1,3) (1,4) (3,4) (2,5)看起来一样的。
输入规模 二维数组A[N][M] N M 都是[1..500], P在[1..10^9]内。数组元素在[1..P]内。
要求复杂度 时间 O(N * MlogM + N * logN) 空间 O(N * M)。
分析: 因为只有相对的指针位置才有意义。我们对每个时钟,指针有小到大排序,时间复杂度O(MlogM),注意,然后计算差值,第一个要减去最后一个。这里有O(M),然后我们需要建立最小表示,也就是说以某个位置开始,把这些指针看成一个圈,形成的字符串字典序最小,这个有O(M)的经典算法,这样我们可以唯一表示一个时钟状态,N个时钟,时间复杂度O(N * MlogM)。 后面我们需要用一个map记录时钟查找时钟,对每个时钟,我们需要查找它出现多少次,查找本身是O(logN)的,但这里判断相等的话,查找N个时钟一共O(NlogN),但这里我们忽略了map一个M个元素的vector的复杂度,感觉上应该还大一些……
代码:
// you can also use includes, for example: // #include <algorithm> #include <algorithm> #include <map> vector<int> make(vector<int> &v) { vector<int> a; int i,j,k,t,n = v.size(); a.resize(n); for (i = k = 0, j = 1; (i < n) && (j < n) && (k < n);) { for (k = 0; (k < n) && ((t = v[(i + k) % n] - v[(j + k) % n]) == 0); ++k) ; if (k < n) { if (t > 0) { i += k + 1; } else { j += k + 1; } if (i == j) { ++j; } } } if (i > j) { i = j; } for (j = 0; j < n; ++j) { a[j] = v[(i + j) % n]; } return a; } int solution(const vector< vector<int> > &A, int P) { // write your code here... int n = A.size(), m = A[0].size(), i,j,answer; vector<int> v,b; map<vector<int>,int> have; map<vector<int>,int>::iterator t; vector<vector<int> > ppmm; if (m == 1) { return n * (n - 1) / 2; } b.resize(m); for (i = answer = 0; i < n; ++i) { v = A[i]; sort(v.begin(), v.end()); for (j = 1; j < m; ++j) { b[j] = v[j] - v[j - 1]; } b[0] = v[0] + P - v[m - 1]; b = make(b); t = have.find(b); if (t == have.end()) { have.insert(make_pair(b, 1)); } else { answer += t->second++; } } return answer; }