CF1007E Mini Metro

题意:有n个车站,每站有初始人数,每一天增加ai人,容纳量为bi。每天你可以派任意辆火车,按顺序接走前K个人。求维持m天最少要多少辆。
\(n,m\leq 200\)

难度较大的DP。
我们发现一旦x车站有人被接走,那么前x-1个车站一定被清空。
利用这个性质可以DP。

设f(x,y,0/1)表示前x个,有/无初始值坚持y时间需要的最小数目。
g(x,y,0/1)表示在f基础上,结束后把前x-1个车站清空的最小代价。
其中不允许接走第x个车站后面的人。
若无解则为inf。
转移较麻烦,可分为如下几步:
枚举z表示最后一次接走i车站的时间。

  1. 在前z个时刻保证合法,并把前x-1个车站清空,即g(x,z,*)。
  2. 算出此时第x个车站剩余人数。并计算第x个车站在剩余的y-z时间内不爆需要的额外车的数目。
注:若需要减到负数则无解。
  3. 剩余的y-z个时间保证前x-1个合法,即f(x-1,y-z,0)。
  4. 对于g的转移,算出结束时前x-1个车站剩余人数,加上清空他们所需代价。

注意:由于最优的f转移在清空前x-1个车站后未必最优,所以我们要分f和g分别计算。
时间复杂度:O(nm^2)。

代码:

#include <stdio.h>
#define ll long long
#define inf 9999999999999999ll
ll A[220],B[220],C[220];
ll ha[220],hb[220],f[220][220][2],g[220][220][2];
int main()
{
    int n,m,K;
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld",&A[i],&B[i],&C[i]);
        A[i]-=B[i];
    }
    A[++n]=inf;C[n]=A[n];
    for(int i=1;i<=n;i++)
        ha[i]=ha[i-1]+A[i],hb[i]=hb[i-1]+B[i];
    for(int i=0;i<=n;i++)
    {
        for(int x=0;x<=m;x++)
        {
            for(int s=0;s<2;s++)
            {
                if(i==0)
                {
                    f[i][x][s]=g[i][x][s]=0;
                    continue;
                }
                f[i][x][s]=g[i][x][s]=inf;
                for(int y=0;y<x;y++)
                {
                    ll t=g[i][y][s];
                    if(t>=inf)continue;
                    ll z=ha[i]*s+hb[i]*(y+1)-t*K;
                    ll ms=C[i]-(x-y)*B[i],c=(z-ms+(K-1))/K;
                    if(c<0)c=0;
                    if(z-c*K>=0)
                    {
                        t=t+c+f[i-1][x-y-1][0];
                        if(t<f[i][x][s])f[i][x][s]=t;
                        ll ss=f[i-1][x-y-1][0];
                        if(ss>=inf)continue;
                        ll tt=hb[i-1]*(x-y)-ss*K;
                        tt=(tt+(K-1))/K;
                        if(tt<0)tt=0;
                        t=g[i][y][s]+c+ss+tt;
                        if(ha[i]*s+hb[i]*(x+1)-t*K>=0&&t<g[i][x][s])
                            g[i][x][s]=t;
                    }
                }
                if(A[i]*s+B[i]*(x+1)<=C[i])
                {
                    ll t=f[i-1][x][s];
                    if(t<f[i][x][s])
                        f[i][x][s]=t;
                    if(t>=inf)continue;
                    ll tt=ha[i-1]*s+hb[i-1]*(x+1)-t*K;
                    tt=(tt+(K-1))/K;
                    if(tt<0)tt=0;
                    t+=tt;
                    if(t*K<=ha[i]*s+hb[i]*(x+1)&&t<g[i][x][s])
                        g[i][x][s]=t;
                }
            }
        }
    }
    ll ans=f[n][m][1];
    printf("%lld\n",ans);
    return 0;
}
posted @ 2022-02-10 13:40  lnzwz  阅读(65)  评论(0编辑  收藏  举报