codility上的问题(24) Psi 2012

给定一个n * n 的格子,边都是导线,M秒,每秒有一根导线被烧断,问从(0,0)到(n - 1, n - 1)在第几秒后不连通?如果一直连通的话,返回-1.

输入为烧掉的边,也就A,B,C如果C= 0 ,烧掉的是(A,B) (A, B + 1) , C = 1烧掉的是(A,B) (A + 1, B)。

于是输入的就是n和3个数组。C的输入比较坑,每个数组给长度,其实A,B,C三个数组都是一样长的嘛。

数据范围: N [1..400] , M [0..2 * N * (N - 1)]  A,B的范围是[0, N - 1] C里面只包含0和1。

要求复杂度: 时间 O(N^2logN) 空间 O(N^2)。

分析: 这个题可以2分时间,然后判断连通性,复杂度达到它的要求,但是我c++用dfs堆栈溢出,bfs超时,双向bfs也超时,结果用c写了一个双向bfs过了……


int e[402][402];
int mark[402][402];
int q[2][80002];
int head[2],tail[2];

int help(int num,int N,int *A,int *B, int *C) {

    const int dx[] = {-1, 0 , 1, 0};
    const int dy[] = {0, 1, 0, -1};

int i,x,y,j,k,xx,yy;

   
    for (i = 0; i < N; ++i) {
        for (j = 0; j < N; ++j) {
            e[i][j] = 15;
            mark[i][j] = -1;
        }
    }
    for (i = 0; i < num; ++i) {
        if (C[i]) {
            e[A[i]][B[i]] &= 11;
            e[A[i] + 1][B[i]] &= 14;
        }
        else {
            e[A[i]][B[i]] &= 13;
            e[A[i]][B[i] + 1] &= 7;
        }
    }
    q[0][0] = 0;
    q[1][0] = ((N - 1) << 9) | (N - 1);
    mark[0][0] = 0;
    mark[N - 1][N - 1] = 1;
    tail[0] = tail[1] = 1;
    head[0] = head[1] = 0;
    for (i = 0; ; i ^= 1) {
        if (head[i] == tail[i]) {
            return 0;
        }
        for (j = tail[i] - head[i]; j; --j) {
            x = q[i][head[i]] >> 9;
            y = q[i][head[i]++] & 511;
            for (k = 0; k < 4; ++k) {
                xx = x + dx[k];
                yy = y + dy[k];
                if ((xx >= 0) && (xx < N) && (yy >= 0) && (yy < N) && (e[x][y] & (1 << k)) && (mark[xx][yy] != i)) {
            		if (mark[xx][yy] == 1 - i) {
                		return 1;
                    }
            		mark[xx][yy] = i;
                    q[i][tail[i] ++] = ((xx << 9) | yy);
                }
            }
        }
    }
    return 0;
        
}
int solution(int N, int A[], int M, int B[], int M2, int C[], int M3) {
    // write your code here...
int left = 1, right = M ,mid;
	while (left <= right) {
        if (help(mid = (left + right) >> 1, N , A, B, C)) {
            left = mid + 1;
        }
        else {
            right = mid - 1;
        }
    }
    return (++right > M)?(-1):right;
}

当然这不是最好的方法,最好的方法是用并查集,我们没有“拆查集”,我们把时间倒过来,从最后一条烧掉的边开始加回来,直到起点和终点相连……并查集的复杂度比log低……

代码:

// you can also use includes, for example:
// #include <algorithm>


vector<int> f;

int getf(int x) {
    return (x == f[x])?x:(f[x] = getf(f[x]));
}

void make(int x,int y) {
	x = getf(x);
    y = getf(y);
    if (x != y) {
       f[x] = y;
    }
}
 
int solution(int N, const vector<int> &A, const vector<int> &B, const vector<int> &C) {
    // write your code here...
    vector<vector<int> > e;
    int i, j, n, m = A.size();
    e.resize(N);
    for (i = 0; i < N; ++i) {
        e[i].resize(N, 15);
    }
    for (i = 0; i < m; ++i) {
        if (C[i]) {
            e[A[i]][B[i]] &= 11;
            e[A[i] + 1][B[i]] &= 14;
        }
        else {
            e[A[i]][B[i]] &= 13;
            e[A[i]][B[i] + 1] &= 7;
        }
    }
    n = N * N;
    f.resize(n);
    for (i = 0; i < n; ++i) {
        f[i] = i;
    }
    for (i = 0; i < N; ++i) {
        for (j = 0; j < N; ++j) {
            if ((i + 1 < N) && (e[i][j] & 4)) {
                make(i * N + j, (i + 1) * N + j);
            }
            if ((j + 1 < N) && (e[i][j] & 2)) {
                make(i * N + j, i * N + j + 1);
            }
        }
        
    }
    if (getf(0) == getf(n - 1)) {
        return -1;
    }
    for (i = m - 1; i >= 0; --i) {
        if (C[i]) {
            make(A[i] * N + B[i], (A[i] + 1) * N + B[i]);
        }
        else {
            make(A[i] * N + B[i], A[i] * N + B[i] + 1);
        }
        if (getf(0) == getf(n - 1)) {
            break;
        }
    }
    return i + 1;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值