单调队列优化的DP——旅行问题

传送门:旅行问题

题意;首先破环成链使用一个长度为2*n的链来做,问题转化成求i以及i+n之间的最小前缀和是否大于零。

顺时针情况下

考虑第i个点时需要计算i到i+n这一端长度为n的区间里面的最小前缀和s[j],(i<=j<=i+n),

如果s[j]-s[i-1]的值小于零的话说明以i为起点不可能环游一周。

代码实现上因为j是大于i的,所以求的是最小的前缀和的值并且长度为n的区间所以要队首元素必须大于等于i+n的;

逆时针情况下

因为使用的还是前缀和数组而不是后缀和,所以s[i]用到o[i],d[i-1],所以d[0]置为d[n].

考虑第i个点时需要计算i到i-n这一段长度为n的区间里面的最大前缀和s[j],(i-n<=j<=i),

如果s[i]-s[j-1]的值小于零的话说明以i为起点不可能逆时针环游一周。

代码:

#include<iostream>
#include<cstring>
#include <cmath>
#include<algorithm>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
const int N=2e6+10;
int n,m,ans,u;
int o[N],d[N];
LL s[N];
bool st[N];
int q[N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&o[i],&d[i]);
        s[i]=s[i+n]=o[i]-d[i];
    }
    for(int i=1;i<=2*n;i++)
        s[i]+=s[i-1];
    int hh=0,tt=0;
    q[0]=2*n+1;
    for(int i=2*n;i>=0;i--)
    {
        if(q[hh]>i+n) hh++;
        if(i<n)
        {
            if(s[i]<=s[q[hh]]) st[i+1]=true;
        }
        while(hh<=tt&&s[q[tt]]>=s[i]) tt--;
        q[++tt] =i;
    }
    d[0]=d[n];
    for(int i=1;i<=n;i++) s[i]=s[i+n]=o[i] -d[i-1];
    for(int i=1;i<=2*n;i++) s[i]+=s[i-1];
    hh=0,tt=0;
    q[0]=0;

    for(int i=1;i<=2*n;i++)
    {
        if(q[hh]<i-n) hh++;
        if(i>n)
        {
            if(s[i]>=s[q[hh]]) st[i-n]=true;
        }
        while(hh<=tt&&s[q[tt]]<=s[i]) tt--;
        q[++tt]=i;
    }
    for(int i=1;i<=n;i++)
        if(st[i])puts("TAK");
    else puts("NIE");
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值