中级农民
- 积分
- 126
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2015-4-22
- 最后登录
- 1970-1-1
|
本帖最后由 zhuli19901106 于 2015-7-1 16:44 编辑
我用并查集写了个,目前还无法确认对错。- /*
- 6 7 3
- 1 3 1 4 2 5
- */
- #include <algorithm>
- #include <cstdio>
- #include <cstring>
- using namespace std;
- int findRoot(int a[], int x)
- {
- int r = x;
- while (r != a[r]) {
- r = a[r];
- }
- int k = x;
- while (x != r) {
- x = a[x];
- a[k] = r;
- k = x;
- }
- return r;
- }
- int solution(int A[], int N, int X, int D)
- {
- int *dj;
-
- dj = new int[X + 1];
- memset(dj, 0, (X + 1) * sizeof(int));
-
- int i;
- for (i = 0; i <= X; ++i) {
- dj[i] = i;
- }
-
- for (i = 1; i <= D && i <= X; ++i) {
- dj[i] = 0;
- }
- if (findRoot(dj, X) == 0) {
- return 0;
- }
-
- int ll, rr;
- i = 0;
- while (i < N && findRoot(dj, X) != 0) {
- if (findRoot(dj, min(A[i] + D, X)) == findRoot(dj, A[i])) {
- ++i;
- continue;
- }
-
- ll = A[i] + 1;
- rr = min(A[i] + D, X);
-
- if (dj[ll] == ll) {
- while (ll <= rr && dj[ll] == ll) {
- dj[ll++] = dj[A[i]];
- }
- } else if (dj[rr] == rr) {
- while (ll <= rr && dj[rr] == rr) {
- dj[rr--] = dj[A[i]];
- }
- } else {
- while (ll <= rr && dj[ll] != ll) {
- ++ll;
- }
- while (ll <= rr && dj[rr] != rr) {
- --rr;
- }
- while (ll <= rr) {
- dj[ll++] = A[i];
- }
- }
-
- if (findRoot(dj, X) == 0) {
- break;
- }
- ++i;
- }
- delete[] dj;
-
- return i < N ? i : -1;
- }
- int main()
- {
- const int MAXN = 100005;
- int *A, N, X, D;
-
- A = new int[MAXN];
- scanf("%d%d%d", &N, &X, &D);
- int i;
- for (i = 0; i < N; ++i) {
- scanf("%d", &A[i]);
- }
- printf("%d\n", solution(A, N, X, D));
- delete[] A;
-
- return 0;
- }
- /*
- int main()
- {
- const int MAXN = 100005;
- const int M = 100000;
- int *A, N, X, D;
-
- A = new int[MAXN];
- N = M;
- X = M;
- D = M / 2;
- int i;
- for (i = 0; i < N; ++i) {
- A[i] = N - 1 - i;
- }
- printf("%d\n", solution(A, N, X, D));
- delete[] A;
-
- return 0;
- }
- */
复制代码 因为写得太繁琐了。。所以代码里没有多boundary cases考虑太多。
大致思路是这样:
比如从在K时刻有片树叶掉到了A[K],那么从A[K] +1 到A[K] + D都可以从A[K]到达,然而你在不知道A[K]能不能到达的情况下,怎么记录这个事实呢,就得用并查集。把A[K] + 1 ~ A[K] + D全部连到A[K]上去
效率关键就在于这个连接的过程上,并查集的path compression不用说,肯定要用到。
我之前试着写了一版,看起来是O(n)复杂度,结果用10万的数据一跑就超时,说明一个问题:如果你碰见每个A[K]都把A[K] + 1 ~ A[K] + D的所有位置都检查一遍,那么复杂度其实是O(N * D) ,而不是O(N)。
所以要尽量避免重复扫描。
看下面这几个情况, 都是两个区间:
比如区间[a, b] [c, d] 表示[a, b]已经被确定为可到达,现在要从c出发,最远跳到d去
[1, 3] [2, 4],由于相交得到[2, 3],那么只用处理[3, 4]即可
[3, 6] [2, 5],[2, 5]被[3, 6]包含,所以如果[3, 6]可到达,那么[2, 5]就不需要处理了,直接跳过
[2, 4] [1, 3],由于相交得到[2, 3],那么只用处理[1, 2]即可
[1, 4] [5, 7] ,不相交,所以[5, 7]全部都要处理
存在左边相交,右边相交,两边相交中间未知,完全不相交四种情况(第四种归到前两种中去)
分别处理即可。
总归,最终目的是判断位置X能否通过并查集一直关联到位置0去即可。
谁给点测试用例?
|
|