CodeM2018 初赛A轮 第四题:迷宫

题意:

一个 nm n ∗ m 的网格迷宫(会有一些墙),现在迷宫内取 K K 个点,两点之间的距离为迷宫内的最短距离,求要形成这K个点的最小生成树(边权即为最短距离),其权值为多少?( n,m2000 n , m ≤ 2000

思路:

这个题思路很容易想出来,就是多源点的BFS,然后其中套一个Kruskal最小生成树算法就行了。比赛中,只过了65%的test case,哎呀,好弱啊。后来才知道,有个地方疏忽了。

一个很容易出错的地方就是:BFS过程中,添加边的顺序,考虑这种情况:O##O###O,就是三个出发点O,中间#表示待访问点(分别编号为12345)。第一轮BFS完后,队列中会有1235这四个点,按道理来说,我们应该先考虑添加距离最短的边(距离为3),即1->2或者2->1,但是可能会出现先把另外一条边(距离为4)先加进去的情况(当队列访问顺序为5321时),这样就会出现问题,所以这种情况应该分开处理。

详见代码吧,思路很简单,就是模拟的时候容易出错。

代码:

#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x7fffffff
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>  P;
typedef vector<int> VI;
typedef map<int,int> MII;
const int N = 2e3 + 10, M = 1e5 + 10;
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
int n, m, k;
LL ans;
char LR[N][N], TB[N][N];
P V[M];
typedef struct state
{
    int x, y, dis, from;
    state(int _x=0, int _y=0, int _from=0, int _dis=0)
    {
        x = _x, y = _y, from = _from, dis = _dis;
    }
}state;

int C, p[M];
int find_set(int x) 
{
    if(x == p[x]) return x;
    return p[x] = find_set(p[x]);
}

int vis[N][N], D[N][N];
void init()
{
    C = k, ans = 0;
    for(int i = 1;i <= C;i ++) p[i] = i;
    memset(vis, -1, sizeof vis);
}
state Q[N*N];
vector<pair<P, P>> Vx;
LL BFS()
{
    int cnt = 0, qh = 0, ql = 0;
    for(int i = 1;i <= k;i ++)
    {
        Q[cnt ++] = state(V[i].FT, V[i].SD, i, 0);
        vis[V[i].FT][V[i].SD] = i, D[V[i].FT][V[i].SD] = 0;
    }
    qh = cnt;
    while(ql < qh)
    {
        for(int o = ql;o < qh;o ++)
        {
            state now = Q[o];
            for(int i = 0;i < 4;i ++)
            {
                int x = now.x + dx[i], y = now.y + dy[i];
                if(x < 1 || x > n || y < 1 || y > m) continue;
                if(i == 0 && TB[x][y] == '1') continue;
                if(i == 1 && TB[now.x][now.y] == '1') continue;
                if(i == 2 && LR[x][y] == '1') continue;
                if(i == 3 && LR[now.x][now.y] == '1') continue;

                if(vis[x][y] == -1)
                {
                    vis[x][y] = now.from, D[x][y] = now.dis + 1;
                    Q[cnt ++] = state(x, y, now.from, now.dis + 1);
                } 
                else
                {
                    int u = find_set(vis[x][y]), v = find_set(now.from);
                    if(u == v) continue;
                    // 容易出错的地方
                    if(D[x][y] == now.dis)
                    {
                        p[u] = v, C --;
                        ans += D[x][y] + now.dis + 1;
                        if(C == 1) return ans;
                    }
                    else Vx.PB({{x, y}, {now.from, now.dis}});
                }
            }
        }
        // 分开处理
        for(int i = 0;i < Vx.size();i ++)
        {
            P t1 = Vx[i].FT, t2 = Vx[i].SD;
            int u = find_set(vis[t1.FT][t1.SD]), v = find_set(t2.FT);
            if(u != v)
            {
                p[u] = v, C --;
                ans += D[t1.FT][t1.SD] + t2.SD + 1;
                if(C == 1) return ans;
            }
        }
        Vx.clear(), ql = qh, qh = cnt;
    }
    return ans;
}

int main()
{
#ifdef YUUKILP
    freopen("in.txt","r",stdin);
#endif
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> k;
    for(int i = 1;i <= n;i ++) cin >> LR[i] + 1;
    for(int i = 1;i < n;i ++)  cin >> TB[i] + 1;
    for(int i = 1;i <= k;i ++) 
    {
        int x, y; cin >> x >> y;
        V[i] = MP(x, y);
    }
    if(k == 1) return cout << 0, 0;
    init();
    return cout << BFS(), 0;
}

官方代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int b13 = 1<<13;

char hmove[int(2e7) + 10];
char vmove[int(2e7) + 10];

int vis[int(2e7) + 10];
int dep[int(2e7) + 10];

int qidx[2000 * 2000 + 10];
int cnt;

int n, m, k;
int ans;

int temp[2000 * 2000 * 2 + 10];
int *temp_a = temp;
int *temp_b = temp + 2000 * 2000 + 5;

inline int check(const int &idx, const int &dir) {
    if (dir == 0)
        return hmove[idx] ? idx+1 : 0;
    else if (dir == 1)
        return hmove[idx-1] ? idx-1 : 0;
    else if (dir == 2)
        return vmove[idx] ? idx+b13 : 0;
    else if (dir == 3)
        return vmove[idx-b13] ? idx-b13 : 0;
}
int root(const int &x) {
    return vis[x] == x ? x : vis[x] = root(vis[x]);
}
int main() 
{
    char s[10010];
    cin >> n >> m >> k;
    for (int i = 0; i < n; ++i) {
        scanf("%s", s);            
        for (int j = 0; j < m-1; ++j) {
            int is_wall = s[j] == '1';
            hmove[(i+1)<<13|(j+1)] = !is_wall;
        }
    }
    for (int i = 0; i < n-1; ++i) {
        scanf("%s", s);
        for (int j = 0; j < m; ++j) {
            int is_wall = s[j] == '1';
            vmove[(i+1)<<13|(j+1)] = !is_wall;
        }
    }
    for (int i = 1; i <= k; ++i) {
        int idx, x, y;
        scanf("%d %d", &x, &y);
        idx = x<<13|y;
        vis[idx] = idx;
        qidx[cnt++] = idx;
    }
    int hp = 0, tp = cnt, curd = 0;
    while (hp < tp) 
    {
        curd ++;
        for (int i = hp; i < tp; ++i) 
        {
            int idx = qidx[i];
            for (int j = 0; j < 4; ++j) 
            {
                int nidx = check(idx, j);
                if (nidx == 0) continue;
                if (vis[nidx]) 
                {
                    if (root(nidx) != root(idx))
                        if (dep[nidx] == dep[idx]) 
                        {
                            ans += curd;
                            vis[root(nidx)] = root(idx);
                        }
                        else 
                        {
                            *(temp_a++) = idx;
                            *(temp_b++) = nidx;
                        }
                }
                else 
                {
                    vis[nidx] = root(idx);
                    dep[nidx] = dep[idx] + 1;
                    qidx[cnt++] = nidx;
                }
            }
        }
        curd ++;
        while (temp_a != temp) 
        {
            --temp_a, --temp_b;
            if (root(*temp_a) != root(*temp_b)) {
                vis[root(*temp_b)] = root(*temp_a);
                ans += curd;
            }
        }
        hp = tp;
        tp = cnt;
    }
    cout << ans << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值