vijos 1243 生产产品
貌似两年前联赛复习的时候就看过这题 然而当时大概看看了 感觉太难 便没有去做
如今再去做的时候 发现其实也并不容易
-------------------------------------------------------------------------
这题首先是要处理一下不能在同一台机器上工作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; }