码迷,mamicode.com
首页 > 其他好文 > 详细

9.8 斯派克

时间:2019-09-11 21:37:32      阅读:79      评论:0      收藏:0      [点我收藏+]

标签:printf   ems   ++i   struct   覆盖   string   operator   strong   编号   

题意

在坐标系中有\(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;
}

9.8 斯派克

标签:printf   ems   ++i   struct   覆盖   string   operator   strong   编号   

原文地址:https://www.cnblogs.com/VeniVidiVici/p/11508777.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!