zoukankan      html  css  js  c++  java
  • 洛谷P3800 Power收集 题解 单调队列优化DP(未完全整理好)

    说明:这篇文章还有很多问题,没有整理好,但是我怕到时候忘了,所以先放上来,等到时候解决了再来更新这篇随笔。

    题目链接:https://www.luogu.com.cn/problem/P3800

    题目大意

    一个 (N imes M) 个二维迷宫,上面有 (K) 个格子上面有宝物,这些宝物有对应的价值,
    你一开始可以选择第一行的任意一个格子进入,然后每次你都会往下走一格,每次往下走一格,你都可以选择在水平方向向左或向右偏移若干个格子,但是最多只能偏移 (T) 个格子。
    你需要寻找一格移动方案,是的从第一行的格子走到第 (N) 行的格子,获得宝物的价值之和最大,输出这个最大价值。

    解题思路

    其实一开始我想到的建图用SPFA求最长路。
    对于第(i)(j) 个宝物,只要 (x_i lt x_j)(|y_i-y_j| ge |x_i-x_j| imes T),则从 (i) 就是可以到达 (j) 的,所以从 (i)(j) 连一条权值为 (v_j) 的边;
    同时,因为所有的点都可能是我第一个到达的点,也都可能是我最后一个到达的点,所以我再设立一个超级源点 (s) 和超级汇点 (t) ,然后从 (s) 向所有的 (i) 连一条权值为 (v_i) 的边,从所有的 (i)(t) 连一条权值为 (0) 的边,然后从 (s)(t) 求最长路。

    然后我因为是用的SPFA求最长路,所以时间复杂度为 (O(V cdot E) = O(K^3)) ,果断超时。

    TLE代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 4040;
    int N, M, K, T, s, t, x[maxn], y[maxn], v[maxn], dist[maxn];
    struct Edge {
        int v, w;
        Edge () {};
        Edge (int _v, int _w) { v = _v; w = _w; }
    };
    vector<Edge> g[maxn];
    queue<int> que;
    bool inq[maxn];
    void spfa() {
        memset(dist, -1, sizeof(dist));
        dist[s] = 0;
        que.push(s);
        while (!que.empty()) {
            int u = que.front();
            que.pop();
            inq[u] = false;
            int sz = g[u].size();
            for (int i = 0; i < sz; i ++) {
                int v = g[u][i].v, w = g[u][i].w;
                if (dist[v] == -1 || dist[v] < dist[u] + w) {
                    dist[v] = dist[u] + w;
                    if (!inq[v]) {
                        inq[v] = true;
                        que.push(v);
                    }
                }
            }
        }
        printf("%d
    ", dist[t]);
    }
    int main() {
        scanf("%d%d%d%d", &N, &M, &K, &T);
        for (int i = 0; i < K; i ++)
            scanf("%d%d%d", x+i, y+i, v+i);
        s = K, t = K+1;
        for (int i = 0; i < K; i ++) {
            g[s].push_back(Edge(i, v[i]));
            g[i].push_back(Edge(t, 0));
        }
        for (int i = 0; i < K; i ++) {
            for (int j = 0; j < K; j ++) {
                if (x[i] < x[j] && abs(y[i] - y[j]) <= T*abs(x[i] - x[j])) {
                    g[i].push_back(Edge(j, v[j]));
                }
            }
        }
        spfa();
        return 0;
    }
    

    然后考虑使用 DP 来解决这个问题。

    定义状态 (v[i][j]) 表示 ((i, j)) 格子的宝物的价值(如果没有宝物则 (v[i][j] = 0)),
    同时定义状态 (f[i][j]) 表示走到 ((i, j)) 能够收获的最大价值,则:

    [f[i][j] = v[i][j] + max_{k in [j-T,j+T]} ( f[i-1][k] ) ]

    而答案就是所有 (f[N][i]) 的最大值。

    然后我们可以用滚动数组优化我们的 (f[N][M]) 的空间为 (f[2][M])

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 4040;
    struct Node {
        int x, y, v;
    } nodes[maxn];
    int N, M, K, T, f[2][maxn], ans;
    bool cmp(Node a, Node b) {
        return a.x < b.x || a.x==b.x && a.y < b.y;
    }
    int main() {
        scanf("%d%d%d%d", &N, &M, &K, &T);
        for (int i = 0; i < K; i ++) scanf("%d%d%d", &nodes[i].x, &nodes[i].y, &nodes[i].v);
        sort(nodes, nodes+K, cmp);
        int c = 0;
        for (int i = 1; i <= N; i ++) {
            for (int j = 1; j <= M; j ++) {
                int v = 0;
                if (nodes[c].x == i && nodes[c].y == j) v = nodes[c++].v;
                if (i == 1) f[i%2][j] = v;
                else {
                    for (int k = max(1, j-T); k <= min(M, j+T); k ++)
                        f[i%2][j] = max(f[i%2][j], f[(i-1)%2][k] + v);
                }
            }
        }
        for (int i = 1; i <= M; i ++) ans = max(ans, f[N%2][i]);
        printf("%d
    ", ans);
        return 0;
    }
    

    但是这样的时间复杂度是 (O(N cdot M cdot K)),所以仍然会超时。

    然后考虑使用 单调队列优化DP

    然后本着不重复造轮子的思想,我要说:我所有的思路都完全来自下面这篇博客:

    https://www.luogu.com.cn/blog/luckyblock/solution-p3800

    按照上述思想,我们能够将时间复杂度优化到 (O(N cdot M))

    实现代码如下:

    
    
  • 相关阅读:
    智能推荐算法演变及学习笔记(三):CTR预估模型综述
    从中国农业银行“雅典娜杯”数据挖掘大赛看金融行业数据分析与建模方法
    智能推荐算法演变及学习笔记(二):基于图模型的智能推荐(含知识图谱/图神经网络)
    (设计模式专题3)模板方法模式
    (设计模式专题2)策略模式
    (设计模式专题1)为什么要使用设计模式?
    关于macOS上常用操作命令(持续更新)
    记录下关于RabbitMQ常用知识点(持续更新)
    EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
    SpringCloud教程二:Ribbon(Finchley版)
  • 原文地址:https://www.cnblogs.com/quanjun/p/12636587.html
Copyright © 2011-2022 走看看