Loading

HDU-6447 YJJ's Salesman DP 树状数组优化

HDU-6447 YJJ's Salesman DP 树状数组优化

题意

在一个网格中,当前要求从\((0,0)\)走到\((10^9,10^9)\)

只能从\((x,y)\) 走到\((x+1,y) or (x,y+1) or (x+1,y+1)\)

现存在\(N\) 个点,到达这些点会增加\(w\)财富,问最终财富的最大值

\[1\leq N \leq 10^5 , w \leq 10^3 \]

分析

容易注意到的性质是,若当前在\((x,y)\) ,那么这个点只能从\((i,j)\) ,其中\(i < x \&\& j < y\)

这表示每行最多选择一个点。

\(dp[i]\) 表示到第\(i\) 行的最大财富值。

\[dp[i] = max(dp[k]) + w_i , k 表示1-(i-1)行可以转移的状态 \]

这里的最大值必须是通过合法状态转移的,不是简单的维护最大值就行,所以这里用树状数组进行区间查询和单点更新是比较方便的。

这样就只需要递推\(N\) 次即可了。至于\(x,y\) 过大,只需要对\(x\) 这一维进行离散化即可。

注意这里的排序,必须对\(x\) 从大到小排序,否则树状数组中进行查询的时候就破坏了之前对角线的性质了。

代码

struct node {
    int x, y, val;
    friend bool operator < (const node& a, const node& b) {
        if (a.y == b.y) return a.x > b.x;
        else return a.y < b.y;
    }
};

node a[100005];
int c[100005], x[100005];
int n;

int ask(int x) {
    int ans = 0;
    for (; x; x -= x & -x) ans = max(ans, c[x]);
    return ans;
}

void update(int x,int val) {
    for (; x <= n; x += x & -x) c[x] = max(c[x], val);
}

int main() {
    int  T = readint();
    while (T--) {
        n = readint();
        for (int i = 0; i <= n; i++) c[i] = 0;
        for (int i = 1; i <= n; i++) {
            a[i].x = readint(), a[i].y = readint(), a[i].val = readint();
            x[i] = a[i].x;
        }
        sort(x + 1, x + n + 1);
        sort(a + 1, a + n + 1);
        int Max = 0;
        for (int i = 1; i <= n; i++) {
            int cnt = lower_bound(x + 1, x + n + 1, a[i].x) - x ;
            int w = a[i].val + ask(cnt - 1);
            Max = max(Max, w);
            update(cnt, w);
        }
        printf("%d\n", Max);
    }
}
posted @ 2020-09-05 21:27  MQFLLY  阅读(115)  评论(0编辑  收藏  举报