【一本通OJ 1600:【例 4】旅行问题】题解
题目链接
题目
原题来自:POI 2004
John 打算驾驶一辆汽车周游一个环形公路。公路上总共有 \(n\) 车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。John 必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。在一开始的时候,汽车内油量为零,John 每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。
任务:判断以每个车站为起点能否按条件成功周游一周。
思路
环形问题难搞,考虑破环成链。
破成一个长度为 \(2n\) 的车站序列,让我们先考虑顺时针的情况。
让我们对汽车油量和距离分别做前缀和 \(s1, s2\),如果从第 \(i\) 个点出发可以回到起点,就意味着在 \([i, i+n-1]\) 的区间内,满足所有油量与距离之差的前缀和都大于等于0,即满足 \((s1_j-s1_{i-1})-(s2_j-s2_{i-1})\geqslant 0\,\,\,(j\in [i, i+n-1])\)。
可是这样子不好维护,于是我们可以尝试对汽车油量与距离之差做前缀和,记为 \(s\)。
有了 \(s\) 数组,我们就可以求区间最小值减去 \(s_{i-1}\) 与0作比较,相当于做一个长度为 \(n\) 的滑动窗口。
逆时针同理。
总结
这道题的思路挺巧妙的。
首先对于这类题,可以先考虑破环成链,这是这类题目的常见思考方向。
然后,我们对于题目的本质进行分析,然后可以想出差值前缀和。
最后,我们发现求最小值的过程中可以使用数据结构,即单调队列进行优化,然后就能做出此题。
Code
// Problem: 1600锛氥€愪緥 4銆戞梾琛岄棶棰?
// Contest: SSOIER
// URL: http://ybt.ssoier.cn:8088/problem_show.php?pid=1600
// Memory Limit: 524 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define mo
#define N 1000010
//#define M
int n, m, i, j, k;
int o[N], d[N], s[N<<1], ans[N];
pair<int, int>p;
deque<pair<int, int> >q;
signed main()
{
// freopen("tiaoshi.in","r",stdin);
// freopen("tiaoshi.out","w",stdout);
n=read();
for(i=1; i<=n; ++i) o[i]=read(), d[i]=read();
for(i=1; i<=2*n; ++i) s[i]=s[i-1]+o[(i-1)%n+1]-d[(i-1)%n+1];
// for(i=1; i<=2*n; ++i) printf("%d ", s[i]); printf("\n");
for(i=1; i<=n; ++i)
{
while(!q.empty() && q.back().first>s[i]) q.pop_back();
p.first=s[i]; p.second=i; q.push_back(p);
}
for(i=n+1; i<=2*n; ++i)
{
ans[i-n]=q.front().first-s[i-n-1];
// printf("ans[%lld]=%lld-%lld=%lld\n", i-n, q.front().first,
// s[i-n-1], q.front().first-s[i-n-1]);
while(!q.empty() && i-q.front().second+1>n) q.pop_front();
while(!q.empty() && q.back().first>s[i]) q.pop_back();
p.first=s[i]; p.second=i; q.push_back(p);
}
q.clear();
for(i=1, j=2*n; i<=2*n; ++i, --j) s[i]=s[i-1]+o[(j-1)%n+1]-d[((j-1>0 ? j-1 : n)-1)%n+1];
// for(i=1; i<=2*n; ++i) printf("%d ", s[i]); printf("\n");
for(i=1; i<=n; ++i)
{
while(!q.empty() && q.back().first>s[i]) q.pop_back();
p.first=s[i]; p.second=i; q.push_back(p);
}
for(i=n+1; i<=2*n; ++i)
{
ans[2*n-i+1]=max(ans[2*n-i+1], q.front().first-s[i-n-1]);
// ans[i-n]=q.front().first-s[i-n-1];
// printf("ans[%lld]=%lld-%lld\n", i-n, q.front().first, s[i-n-1]);
// printf("ans[%lld]=%lld-%lld=%lld\n", 2*n-i+1, q.front().first,
// s[i-n-1], q.front().first-s[i-n-1]);
while(!q.empty() && i-q.front().second+1>n) q.pop_front();
while(!q.empty() && q.back().first>s[i]) q.pop_back();
p.first=s[i]; p.second=i; q.push_back(p);
}
for(i=1; i<=n; ++i) printf(ans[i]>=0 ? "TAK\n" : "NIE\n");
return 0;
}
本文来自博客园,作者:zhangtingxi,转载请注明原文链接:https://www.cnblogs.com/zhangtingxi/p/15901652.html