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))

    实现代码如下:

    
    
  • 相关阅读:
    How ASP.NET MVC Works?[持续更新中…]
    PortalBasic Web 应用开发框架
    .NET性能分析最佳实践之:如何找出使用过多内存的.NET代码(基础篇)
    js模块化开发js大项目代码组织和多人协作的解决之道
    PortalBasic Web 应用开发框架:应用篇(二) —— Action 使用
    细细品味Hadoop_Hadoop集群(第2期)_机器信息分布表
    msmvps.comblogs
    敏捷开发中编写故事应该符合的条件
    自己动手重新实现LINQ to Objects: 12 DefaultIfEmpty
    我的时间管理——充分利用WindowsPhone、Android等设备,实现真正的无压工作!
  • 原文地址:https://www.cnblogs.com/quanjun/p/12636587.html
Copyright © 2011-2022 走看看