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车站的时间。
- 在前z个时刻保证合法,并把前x-1个车站清空,即g(x,z,*)。
- 算出此时第x个车站剩余人数。并计算第x个车站在剩余的y-z时间内不爆需要的额外车的数目。 注:若需要减到负数则无解。
- 剩余的y-z个时间保证前x-1个合法,即f(x-1,y-z,0)。
- 对于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;
}