单调队列优化DP 旅行问题 LibreOJ - 10178

Posted Vincent_0000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单调队列优化DP 旅行问题 LibreOJ - 10178相关的知识,希望对你有一定的参考价值。

题目提交处

点我进入提交

反思

这个题目没有做出来,看到成环的题目没有很敏感,没有想到破环成链。
看了一半题解,知道了大概思路,之后还是没有做出来,对于题目所给的题意没有理清楚。
我们在做题目的时候,不能把自己的思维固定化了,要学会转换思路。

题解

  • 根据题意初步分析
条件对应方案
路线成环破环成链
每升油可以行使一千米以每升油1千米为单位计算
起始点可以选择从右边或者右边出发循环遍历两次
成功条件:途中不能有一个过程没有油。下面需要解决的条件
  • 深入分析

我们接下来的目标是要想出一个办法能够快速判断某一方向是否能成功。
给定一个点 i i i,那么就需要判断区间 [ i , i + n − 1 ] [i,i+n-1] [i,i+n1]是否合法,即判断从 i i i开始的长度不超过 n n n的最小区间和是否大于等于0。公式为: m i n ( s j − s i ) > = 0 min(s_j - s_i) >= 0 min(sjsi)>=0

为什么会这么想呢?
因为我们每个点的权值为 p i − d i p_i - d_i pidi(顺时针计算),我们需要判断每一个过程是否都不小于零,即所有的该区间的前缀和,换个思想,只要找到这些前缀和中最小的一个,如果它都不小于零了,那么其他的肯定也是不小于零的,即是符合条件的。

问题又转化成为了求区间中的最小值,嗯哼,那不就是滑动窗口吗?
那么开始实现代码。

代码实现中有一个细节需要说明一下, 就是我们顺时针遍历数组的时候是反向遍历的,为什么呢?
为了求解维护区间中的值是最小的,所以我们的单调队列需要呈现单调递增(队首是最小的 j j j),而且要保证区间从 i i i开始。
根据单调队列的性质,变化的是在头部,所以我们要反向遍历。

AC代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rep(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl
#define mod(x) (x) % MOD
#define ENDL "\\n"
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 2e6 + 7, MOD = 1e9, INF = 0x3f3f3f3f;
ll s[N];
int p[N], d[N], q[N];
bool vis[N];

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cout.tie(0), cin.tie(0);

    int n;
    cin >> n;
    _for(i, 1, n) cin >> p[i] >> d[i], p[n + i] = p[i], d[n + i] = d[i];
    _for(i, 1, 2 * n) s[i] = s[i - 1] + p[i] - d[i];

    int hh = 0, tt = -1;
    _rep(i, 2 * n, 1) {
        if (hh <= tt && q[hh] > i + n - 1) ++hh;
        while (hh <= tt && s[q[tt]] >= s[i]) --tt;
        q[++tt] = i;
        if (i <= n && s[q[hh]] >= s[i - 1]) vis[i] = true;
    }

    d[0] = d[n];
    _rep(i, 2 * n, 1) s[i] = s[i + 1] + p[i] - d[i - 1];
    hh = 0, tt = -1;
    _for(i, 1, 2 * n) {
        if (hh <= tt && i > q[hh] + n - 1) ++hh;
        while (hh <= tt && s[q[tt]] >= s[i]) --tt;
        q[++tt] = i;
        if (i > n && s[q[hh]] >= s[i + 1]) vis[i - n] = true;
    }

    _for(i, 1, n) puts(vis[i] ? "TAK" : "NIE");
    return 0;
}

以上是关于单调队列优化DP 旅行问题 LibreOJ - 10178的主要内容,如果未能解决你的问题,请参考以下文章

二分+DP+单调队列优化绿色通道 LibreOJ - 10181

Acwing -- 单调队列优化的DP问题

单调队列以及单调队列优化DP

单调队列优化DP

dp优化1——sgq(单调队列)

单调队列优化dp