zoukankan      html  css  js  c++  java
  • 2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest Problem J 【二分+DP+单调队列】

    题目链接

    题意

    有n个地铁站,全部成线性排列,有n-1种地铁票,第i种地铁票的价格为p_i,并且能坐i站(也就是在第k个站能够到达[k-i,k+i]中的站)。现在想从起点站坐到终点站,地铁在相邻两个站之间运行花费1s(这里原文是“get from a stop to the next one in just one minute.”有歧义,坑了好久),给出在每个站出来又进去花费的时间,并且从一个站出来又进去后同一张票又可以重新使用,现给定最大的时间花费,求能够到达终点站的票中最便宜的价格。

    分析

    先考虑这样一个问题,假设固定一次最多能坐到的距离,求到终点的最短时间。很容易想到这样的转移方程:
    (令dp[i]为到第i个站花费的最少时间,d[i]是在该站上下所花时间,r是当前票能够坐的距离)
    dp[i]=dp[x]+d[i]+(ix)
    其中 dp[x]=min{dp[ir]dp[i1]}

    同时考虑到,我们始终是要从起始站坐到终点站的,又因为往回坐始终不是最优的选择,因此地铁运行所带来的时间花费始终是n-1,因此我们在状态转移的时候不考虑它,而在最后时间加上n-1即可

    dp[i]=min{dp[ir]dp[i1]}+d[i]
    最终时间为 dp[n1]+n1

    对于min{dp[ir]dp[i1]},可以用单调队列优化或者直接用线段树,但单调队列在时间上会更优一些。

    那么再考虑买哪一种票,首先能够走得更远但价格便宜的票肯定更优,于是先处理一下,去掉价格贵但走不远的票。这样处理后,所有票都会是按照价格递增,距离递增的顺序出现,二分枚举答案即可。

    时间复杂度: O(nlog(n))(单调队列) O(nlog2(n)) (线段树)

    AC代码

    //2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest Problem J. Journey to the “The World’s Start”
    //AC 2016-9-11 15:02:46
    //DP, Binary Search, Monotonic Queue
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cctype>
    #include <cstdlib>
    #include <cstring>
    #include <vector>
    #include <set>
    #include <string>
    #include <map>
    #include <queue>
    #include <deque>
    #include <list>
    #include <sstream>
    #include <stack>
    using namespace std;
    
    #define cls(x) memset(x,0,sizeof x)
    #define inf(x) memset(x,0x3f,sizeof x)
    #define neg(x) memset(x,-1,sizeof x)
    #define ninf(x) memset(x,0xc0,sizeof x)
    #define st0(x) memset(x,false,sizeof x)
    #define st1(x) memset(x,true,sizeof x)
    #define INF 0x3f3f3f3f
    #define lowbit(x) x&(-x)
    #define input(x) scanf("%d",&(x))
    #define inputt(x,y) scanf("%d %d",&(x),&(y))
    #define bug cout<<"here"<<endl;
    //#pragma comment(linker, "/STACK:1024000000,1024000000")//stack overflow
    #define debug
    
    const int maxn=1000100;
    struct autoque
    {
        long long que[maxn];
        int index[maxn];
        int l;
        int r;
        int k;
        int cur;
        autoque():l(0),r(0),k(0),cur(0){}
        autoque(int kk):l(0),r(0),k(kk),cur(0)
        {
            memset(que,0,sizeof que);
            return;
        }
        void reset(int kk)
        {
            l=0;r=0;k=kk;cur=0;
            memset(que,0,sizeof que);
        }
        long long front()
        {
            while(index[l]<cur-k)
                ++l;
            return que[l];
        }
        void insert(long long x)
        {
            if(l==r)
            {
                que[r]=x;
                index[r++]=cur++;
                return;
            }
            int ll=l,rr=r;
            int mid;
            while(rr>=ll)
            {
                mid=(ll+rr)/2;
                if(rr==ll)
                    break;
                if(x==que[mid])
                    break;
                if(x<que[mid])
                    ll=mid+1;
                if(x>que[mid])
                    rr=mid;
            }
            que[mid]=x;
            index[mid]=cur++;
            r=mid+1;
            return;
        }
    }que;
    
    int n;
    long long t;
    vector<pair<int,int> > p;
    int d[50500];
    long long dp[50500];
    
    bool valid(int x)
    {
        if(x==-1) return 0;
        dp[0]=0;
        que.reset(p[x].first);
        for(int i=1;i<n;++i)
        {
            que.insert(-dp[i-1]);
            dp[i]=-que.front()+d[i];
        }
        return dp[n-1]<=t;
    }
    
    int main()
    {
        //ios::sync_with_stdio(false);
        //cin.tie(0);
        #ifdef debug
            freopen("journey.in","r",stdin);
            freopen("journey.out","w",stdout);
        #endif
        //IO
        cin>>n>>t;
        t-=(n-1);
        pair<int,int> pri;
        for(int i=0;i<n-1;++i)
        {
            input(pri.second);
            pri.first=i+1;
            while(p.size()&&p.back().second>=pri.second)
                p.pop_back();
            p.push_back(pri);
        }
        for(int i=1;i<n-1;++i)
            input(d[i]);
        d[n-1]=0;
        int ans=INF;
        int l=-1,r=p.size()-1,mid=(l+r)>>1;
        while(l<r-1)
        {
            mid=(l+r)>>1;
            if(valid(mid))
                r=mid;
            else
                l=mid;
        }
        cout<<p[r].second<<endl;
        return 0;
    }
  • 相关阅读:
    一些点子的梳理
    网络安全解决方案
    流量劫持技术
    运营商DNS系统安全解决方案
    安卓工作室 设置每次启动 选择项目,不直接打开项目
    多臂机测试, AB测试
    vue 入门
    web开发 入门
    C# .NET 使用第三方类库DotNetZip解压/压缩Zip文件
    Linq 101 工具和源码
  • 原文地址:https://www.cnblogs.com/DrCarlluo/p/6580589.html
Copyright © 2011-2022 走看看