【一本通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;
}
posted @ 2022-02-16 17:30  zhangtingxi  阅读(401)  评论(0编辑  收藏  举报