vijos 1243 生产产品

时间:2021-03-26 17:38:53

貌似两年前联赛复习的时候就看过这题 然而当时大概看看了 感觉太难 便没有去做

如今再去做的时候 发现其实也并不容易 

-------------------------------------------------------------------------

这题首先是要处理一下不能在同一台机器上工作L个步骤

对于这一点 我们可以构造两个数组

$f[i][j]$表示在第$i$台机器上完成了第$j$个步骤

$g[i][j]$表示在第$i$台机器上完成了第$j$个步骤 且第$j-1$个步骤不是在第$i$台机器上完成的

这一个问题解决后 我们通过此题$tag$的提示 会思考一下这题有什么单调性

我们记$sum[i][j]$表示在第$i$台机器上一直工作完第$j$个步骤所耗费时间(不考虑$L$的限制)

对于$f[i][j],f[i][p](p<j)$这两个状态

很明显可以观察出 如果$f[i][p]+(sum[i][j]-sum[i][p])>f[i][j]$

那么从$p$到$j$一定通过换机器达到了更少的时间消耗

将上式移项后可将$f[i][p]-sum[i][p]$作为单调队列中元素大小

每次加入$f[i][j]-sum[i][j]$进行比较

具体实现细节比较多 可以参考代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define rep(i,n) for(int i=1;i<=n;++i)
#define imax(x,y) (x>y?x:y)
#define imin(x,y) (x<y?x:y)
using namespace std;
const int M=100010;
int sum[6][M],f[6][M],g[6][M];
int q[6][M],ifront[6],itail[6];
int m,n,k,l,ans=2147483647;
int main()
{
    scanf("%d%d%d%d",&m,&n,&k,&l);
    int x;
    rep(i,n)
    {
        rep(j,m)
        {
            scanf("%d",&x);
            sum[i][j]=sum[i][j-1]+x;
        }
    }
    memset(f,127,sizeof(f));
    memset(g,127,sizeof(g));
    rep(i,n)
    {
        f[i][1]=g[i][1]=sum[i][1];
        q[i][1]=1;
        ifront[i]=itail[i]=1;
    }
    for(int j=2;j<=m;++j)
    {
        rep(i,n)
        {
            rep(p,5)if(p!=i)
                g[i][j]=imin(g[i][j],f[p][j-1]+(sum[i][j]-sum[i][j-1]+k));
            while(itail[i]>=ifront[i]&&
                  g[i][q[i][itail[i]]]-sum[i][q[i][itail[i]]]>g[i][j]-sum[i][j])
                    --itail[i];
            q[i][++itail[i]]=j;
            if(q[i][ifront[i]]+l-1<j)++ifront[i];
            f[i][j]=imin(f[i][j],g[i][q[i][ifront[i]]]+(sum[i][j]-sum[i][q[i][ifront[i]]]));
        }
    }
    rep(i,n)
    ans=imin(ans,f[i][m]);
    printf("%d",ans);
    return 0;
}