9.8 斯派克

题意

在坐标系中有\(n\)个矩形(保证矩形的四边平行于坐标轴),一个人起始位置在\((0,0)\),需要到达\((X_t,0)\),规定这个人不能穿过任何一个矩形(但可以贴着矩形的边界行走),求最短路线的长度

\(n\leq 5\times 10^5\)

解法

首先有一种行走方式,可以保证最优解一定满足这种行走方式

那就是:在遇到一个矩形时,贴着矩形的边界避开它。这里可以感性的理解一下,应该很好懂

我们还能发现,无论怎么行走,与\(x\)轴平行的路径长度对答案的贡献一定是\(X_t\):不然就会走回头路

在每次避开一个矩形之后,我们一定会来到当前矩形的右上角或右下角,然后等待进行下一步决策

这样我们就可以进行\(DP\)了:设\(DP[x][0/1]\)为当前位于第\(x\)个矩形的右上角\(/\)右下角的答案

那么\(DP[x][0/1]=min(DP[nxt[x][0/1]][0], DP[nxt[x][0/1]][1])\)

这里的\(nxt[x][0/1]\)代表当前位于第\(x\)个矩形的右上角\(/\)右下角,继续向前走所接触到的第一个矩形的编号

若当前的矩形没有\(nxt\)了,那么说明向前走没有障碍物了,那么此时\(DP[x][0/1]=abs(y)\)\(y\)为当前矩阵右上角\(/\)右下角的纵坐标)

这里可以用记忆化搜索的形式实现,很好写

可以发现我们每次搜到一个障碍物,它的答案就确定并被记忆化了,所以可以发现状态数是\(O(n)\)

现在复杂度瓶颈变成了求\(nxt\)数组

朴素的求\(nxt\)数组是\(O(n^2)\)

我们考虑用线段树进行优化

由于\(DP\)中状态只与矩形的右边界有关,我们把所有右边界直线提出来按横坐标排序

将纵坐标离散化,开一颗维护区间覆盖的线段树

倒序枚举所有右边界,每次在线段树中查得一个答案更新区间

复杂度\(O(n \log n)\),具体实现代码很好懂

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e6 + 10;

int n, t, Xt;
int h[N], f[N][2], nxt[N][2];

struct line { 
    int x, u, d;
    bool operator < (const line &T) const { return x < T.x; }
} e[N];

inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }
inline int abs(int x) { return x < 0 ? -x : x; }

void discrete() {
    h[++t] = 0;
    sort(h + 1, h + t + 1);
    t = unique(h + 1, h + t + 1) - h - 1;
}

struct SegTree {
#define ls x << 1
#define rs x << 1 | 1

    int val[N << 2], tag[N << 2];

    void pushdown(int x) {
        if (tag[x]) {
            val[ls] = val[rs] = tag[x];
            tag[ls] = tag[rs] = tag[x];
            tag[x] = 0;
        }
    }

    void update(int x, int l, int r, int ql, int qr, int v) {
        if (ql <= l && r <= qr) {
            val[x] = v, tag[x] = v;
            return;
        }
        int mid = l + r >> 1;
        pushdown(x);
        if (ql <= mid)
            update(ls, l, mid, ql, qr, v);
        if (qr > mid)
            update(rs, mid + 1, r, ql, qr, v);
    }

    int query(int x, int l, int r, int k) {
        if (l == r) return val[x];
        int mid = l + r >> 1;
        pushdown(x);
        if (k <= mid)
            return query(ls, l, mid, k);
        else
            return query(rs, mid + 1, r, k);
    }
    
#undef ls
#undef rs
} tr;

int DFS(int u, int to, int y) {
    if (~f[u][to])  
        return f[u][to];
    if (!nxt[u][to])
        return f[u][to] = abs(y);
    int ne = nxt[u][to];
    int up = abs(e[ne].u - y), dw = abs(y - e[ne].d);   
    up += DFS(nxt[u][to], 0, e[ne].u);
    dw += DFS(nxt[u][to], 1, e[ne].d);
    return f[u][to] = min(up, dw);
}

int main() {

    scanf("%d%d", &n, &Xt);

    int a, b, c, d;
    for (int i = 1; i <= n; ++i) {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        e[i] = (line){max(a, c), max(b, d), min(b, d)};
        h[++t] = b, h[++t] = d;
    }

    discrete();

    sort(e + 1, e + n + 1);
    for (int i = n; i >= 1; --i) {
        int a = lower_bound(h + 1, h + t + 1, e[i].d) - h;
        int b = lower_bound(h + 1, h + t + 1, e[i].u) - h;
        // printf("va: %d a: %d vb: %d b: %d\n", e[i].u, a, e[i].d, b);
        nxt[i][0] = tr.query(1, 1, t, b);
        nxt[i][1] = tr.query(1, 1, t, a);
        tr.update(1, 1, t, a, b, i);
    }

    int id = lower_bound(h + 1, h + t + 1, 0) - h;
    nxt[0][0] = nxt[0][1] = tr.query(1, 1, t, id);

    // for (int i = 0; i <= n; ++i)    printf("%d %d %d\n", i, nxt[i][0], nxt[i][1]);

    memset(f, -1, sizeof f);
    printf("%d\n", Xt + DFS(0, 0, 0));

    return 0;
}

转载于:https://www.cnblogs.com/VeniVidiVici/p/11508777.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
校园失物招领系统管理系统按照操作主体分为管理员和用户。管理员的功能包括字典管理、论坛管理、公告信息管理、失物招领管理、失物认领管理、寻物启示管理、寻物认领管理、用户管理、管理员管理。用户的功能等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 校园失物招领系统管理系统可以提高校园失物招领系统信息管理问题的解决效率,优化校园失物招领系统信息处理流程,保证校园失物招领系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 ,管理员权限操作的功能包括管理公告,管理校园失物招领系统信息,包括失物招领管理,培训管理,寻物启事管理,薪资管理等,可以管理公告。 失物招领管理界面,管理员在失物招领管理界面中可以对界面中显示,可以对失物招领信息的失物招领状态进行查看,可以添加新的失物招领信息等。寻物启事管理界面,管理员在寻物启事管理界面中查看寻物启事种类信息,寻物启事描述信息,新增寻物启事信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值