zoukankan      html  css  js  c++  java
  • Gym 100801 J. Journey to the “The World’s Start” DP+单调队列优化+二分

       http://codeforces.com/gym/100801

    题目大意:有从左到右有n个车站,有n-1种车票,第i种车票一次最多可以坐 i 站(1<=i<=n)   每种票有固定的价钱p[i],下车后可以继续上车,但每次下车都要再上车都要花费一定时间v[i],第1和n站上车不花费时间,车从一站到相邻的一站需要1分钟。只能买一种车票,问在t时间内 所有能从第1站到 第n站的方案  花费 的票价最低是多少。

    解题思路: 
    这道题的关键在于如何求每种票起点到达终点所需的最少时间,如果找出了每种票到车站的最少时间,再直接判断哪种票最便宜就好了。

    对于求每种票的时间,最直观的想法是对每一种票进行dp,设dp[i]为到i站所需的最少时间,k为此票最多能乘坐的站数,v[i]为在此站下车又上车的时间。 
    dp[i]=min(dp(i-j))+v[i] 1<=j<=i-k 
    对于n-1种票,每种票枚举n个车站,枚举到每个车站时还要向前推k个,复杂度为O(n^3)

    但我们会发现,如果 坐n站的车票,能够在t时间内到达 。那么 坐n+1站的车票,也能够在t时间内到达。 这样我们就可以二分查找所需时间小于等于t-(n-1)的车票中能坐站数最少的车票就好了。(规定时间内能够到达的车票分界线)复杂度n^2*logn 还是会超时

    对于每次dp,我们观察状态方程发现最佳状态是找dp[i-j](1<=j<=i-k)区间中的最小值,每次查找时间是o(k) 。其实我们可以维护一个长度为k的单调队列来找最小值。复杂度o(n*logn) ,解决。

    AC代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn = 1e5+10 ,mod = 998244353,inf=0x3f3f3f3f;
     4 const double pi=acos(-1.0);
     5 typedef long long ll;
     6 ll dp[maxn];
     7 ll p[maxn],v[maxn],n,m;
     8 struct node
     9 {
    10     ll val,pos;
    11 }q[maxn];
    12 ll solve(ll k)
    13 {
    14     memset(dp,inf,sizeof(dp));
    15     ll head=1,tail=0;
    16     for(ll i=1;i<=k;i++)   //前k个的最优解就是v[i]
    17     {
    18         dp[i]=v[i];
    19         while(head<=tail&&dp[i]<=q[tail].val)tail--;
    20         q[++tail].val=dp[i],q[tail].pos=i;
    21     }
    22     //cout<<tail<<endl;
    23     for(ll i=k;i<=n;i++)   
    24     {
    25         while(i-q[head].pos>k)head++;     //注意下标范围
    26         dp[i]=min(dp[q[head].pos]+v[i],dp[i]);
    27         while(head<=tail&&dp[i]<=q[tail].val)tail--;   //维护单调性
    28         q[++tail].val=dp[i],q[tail].pos=i;
    29        //for(int j=1;j<=k;j++)                 //未优化之前代码
    30        // {
    31        //     if(i-j>0)
    32        //     dp[i]=min(dp[i-j]+v[i],dp[i]);
    33        // }
    34     }
    35     return dp[n];
    36 }
    37 int main()
    38 {
    39     freopen("journey.in","r",stdin);    //交题要用到文件流
    40     freopen("journey.out","w",stdout);
    41     scanf("%lld %lld",&n,&m);
    42     for(ll i=1;i<=n-1;i++)
    43         scanf("%lld",&p[i]);
    44     v[1]=v[n]=0;
    45     for(ll i=2;i<=n-1;i++)
    46         scanf("%lld",&v[i]);
    47     m-=n-1;
    48     ll l=1,r=n-1;
    49     while(l<=r)   //二分
    50     {
    51         ll mid=(l+r)/2;
    52         if(solve(mid)>m)
    53             l=mid+1;
    54         else
    55             r=mid-1;
    56     }
    57     ll ans=inf;
    58     for(ll i=l;i<=n-1;i++)  //找到分界点从右边区间找最小值
    59     {
    60         ans=min(ans,p[i]);
    61     }
    62     printf("%lld
    ",ans);
    63 }
  • 相关阅读:
    centos8 将SSSD配置为使用LDAP并要求TLS身份验证
    Centos8 搭建 kafka2.8 .net5 简单使用kafka
    .net core 3.1 ActionFilter 拦截器 偶然 OnActionExecuting 中HttpContext.Session.Id 为空字符串 的问题
    Springboot根据不同环境加载对应的配置
    VMware Workstation12 安装 Centos8.3
    .net core json配置文件小结
    springboot mybatisplus createtime和updatetime自动填充
    .net core autofac依赖注入简洁版
    .Net Core 使用 redis 存储 session
    .Net Core 接入 RocketMQ
  • 原文地址:https://www.cnblogs.com/stranger-/p/8998762.html
Copyright © 2011-2022 走看看