保研机试——巨大坐标系模板

决策导航:第一步,看数据范围!

拿到题目,首先看 N, M(行、列)的范围:

  1. 稠密图 / 小棋盘 (Dense Grid)

    • 特征N, M 通常在 3000 以下,总格子数 N * M10^7 以内。
    • 决策毫不犹豫,使用 g[N][N] 二维数组。 这是最快、最直观、代码最简单的选择。
    • 跳转至 -> 第一部分:稠密图模板
  2. 稀疏图 / 巨大坐标系 (Sparse Grid)

    • 特征N, M 非常大(例如 105,10910^5, 10^9105,109 甚至更大),但有意义的点(初始点 c、操作数 T)的数量是可控的(例如 105∼10610^5 \sim 10^6105106)。
    • 决策必须使用 map 系列。g[N][N] 会直接导致内存超限 (MLE) 或运行时错误 (RE)。
    • 跳转至 -> 第二部分:稀疏图模板

第一部分:稠密图 / 小棋盘模板 (g[N][N])

A. 基础操作
const int N = 3005;
int g[N][N];
bool visited[N][N]; // 遍历时使用

// 1. 初始化
void init_dense() {
    memset(g, 0, sizeof(g));
    memset(visited, false, sizeof(visited));
}

// 2. 访问与修改
void modify_dense(int x, int y, int value) {
    g[x][y] = value;
}

int read_dense(int x, int y) {
    return g[x][y];
}
B. 遍历 (Traversal) - 核心

1. 广度优先搜索 (BFS) - 用于找最短路径(步数最少)

int dist[N][N]; 

// 方向数组: 上, 右, 下, 左 (4方向)
const int dx4[] = {-1, 0, 1, 0};
const int dy4[] = {0, 1, 0, -1};

// 方向数组: 8方向 (包括对角线)
const int dx8[] = {-1, -1, 0, 1, 1, 1, 0, -1};
const int dy8[] = {0, 1, 1, 1, 0, -1, -1, -1};

void bfs(int start_x, int start_y, int n, int m) {
	memset(dist, -1, sizeof(dist)); // -1 表示未访问
	memset(visited, false, sizeof(visited));
    queue<pair<int, int>> q;
    
    q.push({start_x, start_y});
    visited[start_x][start_y] = true;
    dist[start_x][start_y] = 0;
    
    while (!q.empty()) {
        pair<int, int> pos = q.front();
        q.pop();
        int x = pos.first;
        int y = pos.second;
        
        // 在这里处理当前点 (x, y) 的逻辑
        
        // 遍历4个或8个方向
        for (int i = 0; i < 4; ++i) { // 或 i < 8
            int nx = x + dx4[i]; // 或 dx8[i]
            int ny = y + dy4[i]; // 或 dy8[i]
            
            // 检查边界
            if (nx < 1 || nx > n || ny < 1 || ny > m) continue;
            // 检查是否已访问
            if (visited[nx][ny]) continue;
            // 检查是否是障碍物等其他逻辑
            // if (g[nx][ny] == OBSTACLE) continue;
            
            visited[nx][ny] = true;
            dist[nx][ny] = dist[x][y] + 1; // 更新距离
            q.push({nx, ny});
        }
    }
}

2. 深度优先搜索 (DFS) - 用于查找所有可达点、连通分量

void dfs(int x, int y, int n, int m) {
    visited[x][y] = true;
    
    // 在这里处理当前点 (x, y) 的逻辑

    for (int i = 0; i < 4; ++i) { // 或 i < 8
        int nx = x + dx4[i]; // 或 dx8[i]
        int ny = y + dy4[i]; // 或 dy8[i]

        if (nx < 1 || nx > n || ny < 1 || ny > m) continue;
        if (visited[nx][ny]) continue;
        // if (g[nx][ny] == OBSTACLE) continue;
        
        dfs(nx, ny, n, m);
    }
}

第二部分:稀疏图 / 巨大坐标系模板 (map)

A. 核心数据结构
map<pair<int, int>, T> g; // 存储点的值
map<int, set<int>> rows;            // 行号 -> 该行所有点的列号集合
map<int, set<int>> cols;            // 列号 -> 该列所有点的行号集合
map<int, set<int>> diag1;           // 对角线key: (y-x) -> x坐标集合
map<int, set<int>> diag2;           // 反对角key: (y+x) -> x坐标集合
B. 基础操作 (增删改查)
// 添加点
void add_point(int x, int y, T value) {
    g[{x, y}] = value;
    rows[x].insert(y);
    cols[y].insert(x);
    diag1[y - x].insert(x);
    diag2[y + x].insert(x);
}

// 删除点
void remove_point(int x, int y) {
    g.erase({x, y});
    if(rows.count(x)) rows[x].erase(y);
    if(cols.count(y)) cols[y].erase(x);
    if(diag1.count(y - x)) diag1[y - x].erase(x);
    if(diag2.count(y + x)) diag2[y + x].erase(x);
}

// 读取点的值
T get_value(int x, int y) {
	T default_value = //不存在点的默认值
    if (g.count({x, y})) {
        return g.at({x, y});
    }
    return default_value;
}
C. 邻居查找模板 (核心)

1. 查找行/列邻居

// 查找 (r, c) 上方的第一个点
pair<int, int> find_up(int r, int c) {
    if (!cols.count(c)) return {0, 0};
    auto& col_set = cols.at(c);
    auto it = col_set.lower_bound(r);
    return (it != col_set.begin()) ? make_pair(*prev(it), c) : make_pair(0, 0);
}

// 查找 (r, c) 下方的第一个点
pair<int, int> find_down(int r, int c) {
    if (!cols.count(c)) return {0, 0};
    auto& col_set = cols.at(c);
    auto it = col_set.upper_bound(r);
    return (it != col_set.end()) ? make_pair(*it, c) : make_pair(0, 0);
}

// 查找左/右与上/下逻辑相同,操作rows即可
pair<int, int> find_left(int r, int c) {
    if (!rows.count(r)) return {0, 0};
    auto& row_set = rows.at(r);
    auto it = row_set.lower_bound(c);
    return (it != row_set.begin()) ? make_pair(r, *prev(it)) : make_pair(0, 0);
}
pair<int, int> find_right(int r, int c) {
    if (!rows.count(r)) return {0, 0};
    auto& row_set = rows.at(r);
    auto it = row_set.upper_bound(c);
    return (it != row_set.end()) ? make_pair(r, *it) : make_pair(0, 0);
}

2. 查找对角线邻居

// 查找 (r, c) 左上方的第一个点
pair<int, int> find_up_left(int r, int c) {
    int d_key = c - r;
    if (!diag1.count(d_key)) return {0, 0};
    auto& diag_set = diag1.at(d_key);
    auto it = diag_set.lower_bound(r); // 对角线上的点用行号r或列号c排序均可
    if (it == diag_set.begin()) return {0, 0};
    int prev_r = *prev(it);
    return {prev_r, prev_r + d_key}; // y = x + (y-x)
}

// 查找 (r, c) 右上方的第一个点
pair<int, int> find_up_right(int r, int c) {
    int ad_key = c + r;
    if (!diag2.count(ad_key)) return {0, 0};
    auto& anti_diag_set = diag2.at(ad_key);
    auto it = anti_diag_set.lower_bound(r);
    if (it == anti_diag_set.begin()) return {0, 0};
    int prev_r = *prev(it);
    return {prev_r, ad_key - prev_r}; // y = (y+x) - x
}

// 查找 (r, c) 右下方的第一个点
Point find_down_right(int r, int c) {
    int d_key = c - r;
    if (!diag1.count(d_key)) return {0, 0};
    auto& diag_set = diag1.at(d_key);
    auto it = diag_set.upper_bound(r);
    if (it == diag_set.end()) return {0, 0};
    int next_r = *it;
    return {next_r, next_r + d_key};
}

// 其他对角线方向以此类推...
D. 稀疏图遍历

类型1:稀疏点,但连接关系是稠密的(邻接)

void bfs_sparse(int start_x, int start_y) {
    if (!g.count({start_x, start_y})) return; // 起点不存在

    queue<pair<int, int>> q;
    set<pair<int, int>> visited; // 稀疏图的 visited 也必须是稀疏的

    q.push({start_x, start_y});
    visited.insert({start_x, start_y});

    while(!q.empty()){
        pair<int, int> pos = q.front();
        q.pop();

        // 在这里处理点 pos 的逻辑

        // 查找邻居的方式不再是 dx, dy
        // 而是查询 map 中是否存在直接相邻的点
        const int dx[] = {-1, 0, 1, 0};
        const int dy[] = {0, 1, 0, -1};
        for(int i=0; i<4; ++i){
            int nx = pos.first + dx[i];
            int ny = pos.second + dy[i];

            // 检查该邻居是否存在于图中,并且没有被访问过
            if(g.count({nx, ny}) && !visited.count({nx, ny})){
                visited.insert({nx, ny});
                q.push({nx, ny});
            }
        }
    }
}

类型2:稀疏点,连接关系是跳跃的(行/列/对角线的下一个点)

// 场景:一个点可以“跳”到它所在行/列/对角线的下一个存在的点
// 比如激光在镜子间反射、棋子移动等
void bfs_sparse_line_of_sight(Point start_pos) {
    if (!g.count(start_pos)) return;

    queue<Point> q;
    map<Point, int> dist; // 用 map 同时作为 visited 和 距离记录

    q.push(start_pos);
    dist[start_pos] = 0;

    while(!q.empty()){
        Point pos = q.front();
        q.pop();
        int r = pos.first;
        int c = pos.second;

        // 在这里处理点 pos 的逻辑,比如 if (pos == end_pos) return dist[pos];

        // 将所有可能的“下一个邻居”找出
        Point neighbors[8];
        neighbors[0] = find_up(r, c);
        neighbors[1] = find_down(r, c);
        neighbors[2] = find_left(r, c);
        neighbors[3] = find_right(r, c);
        // neighbors[4] = find_up_left(r, c); ... (根据题目要求添加4或8方向)
        // ...

        for (const auto& next_pos : neighbors) {
            // 检查邻居是否有效,并且没有被访问过
            if (next_pos != INVALID_POS && !dist.count(next_pos)) {
                dist[next_pos] = dist[pos] + 1;
                q.push(next_pos);
            }
        }
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看烟花的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值