zoukankan      html  css  js  c++  java
  • [2019HDU多校第一场][HDU 6580][C. Milk]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6580

    题目大意:(n imes m)大小的方格上有(k)瓶水,喝完每瓶水都需要一定的时间。初始点在((1,1)),每次可以向左或者向右走一步,如果当前的纵坐标为(frac{m+1}{2})则可以向下走一步。对所有的(i in [1,k]),求喝恰好(i)瓶水需要花费的时间。(n,mleq 10^9, kleq 10^4)

    题解:首先对瓶子的横坐标离散化处理,一行一行地更新答案。每到新的一行,先预处理出向左(右)走喝了(i)瓶水后,回到原点以及不回到原点所需要的最短时间,分别记为(l,r,l\_back,r\_back)。然后将左右两边合并,求出喝了(i)瓶水后,回到或不回到原点所需时间,记为(g,g\_back)。设(f[i])为从((1,1))出发,喝了(i)瓶水后回到当前行中点所需的最短时间,则这时就可以根据(g)来和之前的(f[i])来更新答案,并用(g\_back)来更新(f)的值。时间复杂度为(O(k^2))

       对于每一行(l,r,l\_back,r\_back)数组的预处理,可以考虑先对喝水所需时间进行排序,枚举最远走到哪里。当最远到达的点确定后,就可以只考虑喝水所需要的时间来更新状态了

       具体实现见代码,注意如果当前行为(1)时要特殊处理

    #include<bits/stdc++.h>
    using namespace std;
    #define N 10005
    #define LL long long
    #define MP make_pair
    LL T,n,m,k,f[N],ans[N];
    LL l[N],l_back[N],r[N],r_back[N],g[N];
    LL g_back[N],nxt[N];
    pair<LL,LL>cost[N];
    map<LL,vector<pair<LL,LL>>>mp;
    void init()
    {
        mp.clear();
        scanf("%lld%lld%lld",&n,&m,&k);
        for(int i=1;i<=k;i++)
          f[i]=ans[i]=1e18;
        for(int i=1;i<=k;i++)
          {
          LL R,c,t;
          scanf("%lld%lld%lld",&R,&c,&t);
          mp[R].push_back(MP(c,t));
          }
        f[0]=ans[0]=m-1>>1;
        LL lst=1,mx=0;
        for(const auto &pi:mp)
          {
          LL R=pi.first;
          auto d=pi.second;
          d.push_back(MP(0,0));
          sort(d.begin(),d.end());
          LL n=d.size()-1,cnt=0,mid=m+1>>1;
          for(int i=1;i<=n;i++)
            if(d[i].first<mid)cnt++;
          d[0].first=mid;
          if(R==1)//对当前行为1的特判 
            {
            nxt[0]=1;
            for(int i=1;i<=n;i++)
              nxt[i]=i+1,r[i]=r_back[i]=1e18;
            for(int i=1;i<=n;i++)
              cost[i]=MP(d[i].second,i);
            sort(cost+1,cost+n+1);
            for(int i=n;i>=1;i--)
              {
              LL sum=0,id=0;
              for(int j=nxt[0],k=1;j<=n;j=nxt[j],k++)
                {
                sum+=cost[j].first;
                r[k]=min(r[k],sum+d[i].first-1);
                if(nxt[j]<=n && cost[nxt[j]].second==i)id=j;
                r_back[k]=min(r_back[k],sum+2*max(d[i].first,mid)-mid-1);
                }
              nxt[id]=nxt[nxt[id]];
              }
            for(int i=1;i<=n;i++)
              {
              ans[i]=min(ans[i],r[i]);
              f[i]=min(f[i],r_back[i]);
              }
            mx=n;
            continue;
            }
          nxt[0]=1;
          for(int i=1;i<=cnt;i++)
            {
            cost[i]=MP(d[i].second,i);
            l[i]=l_back[i]=1e18;
            nxt[i]=i+1;
            }
          sort(cost+1,cost+cnt+1);//对喝水耗时进行排序 
          for(int i=1;i<=cnt;i++)//枚举最远走到哪,从远到近枚举 
            {
            LL sum=0,id=0;
            for(int j=nxt[0],k=1;j<=cnt;j=nxt[j],k++)//取前k小的喝水耗时,更新答案 
              {
              sum+=cost[j].first;
              l[k]=min(l[k],sum+mid-d[i].first);
              if(nxt[j]<=cnt && cost[nxt[j]].second==i)id=j;
              l_back[k]=min(l_back[k],sum+2*(mid-d[i].first));
              }
            nxt[id]=nxt[nxt[id]];//删除当前最远点 
            }
          nxt[cnt]=cnt+1;
          for(int i=cnt+1;i<=n;i++)
            {
            cost[i]=MP(d[i].second,i);
            r[i-cnt]=r_back[i-cnt]=1e18;
            nxt[i]=i+1;
            }
          sort(cost+cnt+1,cost+n+1);
          for(int i=n;i>cnt;i--)
            {
            LL sum=0,id=cnt;
            for(int j=nxt[cnt],k=1;j<=n;j=nxt[j],k++)
              {
              sum+=cost[j].first;
              r[k]=min(r[k],sum+d[i].first-mid);
              if(nxt[j]<=n && cost[nxt[j]].second==i)id=j;
              r_back[k]=min(r_back[k],sum+2*(d[i].first-mid));
              }
            nxt[id]=nxt[nxt[id]];
            }
          for(int i=1;i<=n;i++)//合并l, r 
            {
            g[i]=g_back[i]=1e18;
            for(int j=max(0ll,i-(n-cnt));j<=min(1ll*i,cnt);j++)
              {
              g[i]=min(g[i],min(l[j]+r_back[i-j],l_back[j]+r[i-j]));
              g_back[i]=min(g_back[i],min(l_back[j]+r_back[i-j],l_back[j]+r_back[i-j]));
              }
            }
          for(int i=0;i<=mx;i++)//更新ans 
            {
            f[i]+=R-lst;//先加上走过的行数 
            for(int j=1;j<=n;j++)
              ans[i+j]=min(ans[i+j],f[i]+g[j]);
            }
          for(int i=mx;i>=0;i--)//更新f 
            for(int j=n;j>=1;j--)
              f[i+j]=min(f[i+j],f[i]+g_back[j]);
          mx+=n,lst=R;//更新上一次走到的行数 
          }
        for(int i=1;i<=k;i++)
          printf("%lld%c",ans[i],i<k?' ':'
    ');
    }
    int main()
    {
        scanf("%lld",&T);
        while(T--)init();
    }
    View Code
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法训练 排列问题
    Java实现 蓝桥杯VIP 算法训练 排列问题
    Java实现 蓝桥杯VIP 算法训练 排列问题
    Java实现 蓝桥杯VIP 算法训练 排列问题
    关于模态/非模态对话框不响应菜单的UPDATE_COMMAND_UI消息(对对WM_INITMENUPOPUP消息的处理)
  • 原文地址:https://www.cnblogs.com/DeaphetS/p/11240823.html
Copyright © 2011-2022 走看看