一本通 1600:【例 4】旅行问题

题目描述

John 打算驾驶一辆汽车周游一个环形公路。
公路上总共有 n个车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。
John 必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。
在一开始的时候,汽车内油量为零,John每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。
任务:判断以每个车站为起点能否按条件成功周游一周。

输入样例

5
3 1
1 2
5 2
0 1
5 4

输出样例

TAK
NIE
TAK
NIE
TAK

算法1

(暴力模拟) \(O(n^2)\)

承受不起...

C++ 代码

不会QAQ

算法2

(堆优化) \(O(nlogn)\)

其实和正解有点像了:
我们不妨合并成一个a数组,使a[i]=p[i]-d[i],表示油量的增减情况。
我们可以维护一个前缀和pre来存储,那么我们要走的路就可以通过差分来求出,我们要判断一条路能否走成功,那么就要看这条路中油最少的情况是否大于0。
我们就可以使用堆来存储。
不过还是会超.

C++ 代码

不会QAQ

正解

(单调队列优化)

将环给拆成一条链跑.
正反跑两次,如果维护路上前缀和,内容是每一个加油站的贡献,也就是p-d.如果路上有一个前缀和小于0了,那么这种方式就不行,正反跑两遍就行.

c++ 代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
const int INF=0x3f3f3f3f;
int n;
ll s[N<<1];
bool ans[N];
int head,tail;
int p[N],d[N],a[N<<1],q[N];
int main() {
    scanf("%d",&n);
    for(int i=1; i<=n; i++) scanf("%d%d",&p[i],&d[i]);
    d[0]=d[n];
    for(int i=1; i<=n; i++) a[i]=a[n+i]=p[i]-d[i];
    for(int i=1; i<n<<1; i++) s[i]=s[i-1]+a[i];
    head=1,tail=0;
    for(int i=1; i<n<<1; i++) {
        while(head<=tail&&i-n>=q[head]) ++head;
        while(head<=tail&&s[q[tail]]>=s[i]) --tail;
        q[++tail]=i;
        if(i>=n&&s[q[head]]>=s[i-n]) ans[i-n+1]=1;
    }
    for(int i=1; i<=n; i++) a[i]=a[i+n]=p[n-i+1]-d[n-i];
    for(int i=1; i<n<<1; i++) s[i]=s[i-1]+a[i];
    head=1,tail=0;
    for(int i=1; i<n<<1; i++) {
        while(head<=tail&&i-n>=q[head]) ++head;
        while(head<=tail&&s[q[tail]]>=s[i]) --tail;
        q[++tail]=i;
        if(i>=n&&s[q[head]]>=s[i-n]) ans[(n<<1)-i]=1;
    }
    for(int i=1; i<=n; i++) {
        if(ans[i]) puts("TAK");
        else puts("NIE");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Fast-Bird/p/11851523.html