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 }
  • 相关阅读:
    数据提交方式:post和get
    什么是SQL注入式攻击和如何防范?
    CSS3 @keyframes 规则
    php数据库连接及简单操作
    深入理解CSS过渡transition
    css 选择器
    利用border制作三角形原理
    iOS 8 自适应 Cell
    dSYM 文件分析工具
    iOS开发使用半透明模糊效果方法整理
  • 原文地址:https://www.cnblogs.com/stranger-/p/8998762.html
Copyright © 2011-2022 走看看