vijos 1243 生产产品 DP + 单调队列优化

时间:2022-02-19 17:37:41

LINK

题意:有1个产品,m个步骤编号为1~m。步骤要在n个机器人的手中生产完成。其中,第i个步骤在第j个机器人手中的生产时间给定为$T[i][j]$,切换机器人消耗cost。步骤必须按顺序,同一个机器人不能连续完成超过l个步骤。求完成所有步骤的最短时间是多少。其中$m<=10^5$,$n<=5$,$l<=5*10^4$

思路:这题用DP考虑易得一个转移方程$dp[i][j]=\min^{i-1}_{v=i-L}{(dp[v][x] + sum[i][j] - sum[v][j]) + cost}$其中$sum[i][j]$代表前i步由j完成的时间和

从转移式上来看,j为要转移到的状态,x为出发态,相当于对每个步骤v都做$O(n^2)$的转移,直接暴力枚举的话总复杂度是$O(l·m·n^2)$

当L足够大时,显然这样的算法不够快,可以注意到v的枚举是顺序的,而且$dp[v][x] -sum[v][j]$在给定x和j时也满足单调性的要求,即当$dp[v][x] -sum[v][j]$得到更小的值后,最优值为新得到的值,那么使用二维优先队列维护从x机器人转移到j机器人,范围为L的优先队列,队列存储其划分的位置,这样一来我们可在$O(1)$复杂度内获得L范围内的最小值(队列中元素最多进出一次),优化后复杂度$O(m·n^2)$

 

 

/** @Date    : 2017-07-19 19:25:11
* @FileName: vijos 1243 单调性优化 DP 双端队列.cpp
* @Platform: Windows
* @Author : Lweleth (SoungEarlf@gmail.com)
* @Link : https://github.com/
* @Version : $Id$
*/
#include <bits/stdc++.h>
#define LL long long
#define PII pair<int ,int>
#define MP(x, y) make_pair((x),(y))
#define fi first
#define se second
#define PB(x) push_back((x))
#define MMG(x) memset((x), -1,sizeof(x))
#define MMF(x) memset((x),0,sizeof(x))
#define MMI(x) memset((x), INF, sizeof(x))
using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1e5+20;
const double eps = 1e-8;

int n, m, cost, l;
int sum[100500][8];
int dp[100500][8];

int main()
{
while(cin >> m >> n >> cost >> l)
{
int x;
for(int i = 1; i <= n; i++)
{
sum[0][i] = 0;
for(int j = 1; j <= m; j++)
{
scanf("%d", &x);
sum[j][i] = sum[j - 1][i] + x;
}
}

deque<int >q[8][8];
for(int i = 0; i <= m; i++)
for(int j = 0; j <= n; j++)
dp[i][j] = INF;
for(int j = 0; j <= n; j++)
dp[0][j] = 0;
/*for(int i = 1; i <= n; i++)
for(int k = 1; k <= n; k++)
q[i][k].push_back(0);*/
///////////////////////
for(int i = 0; i <= m; i++)
{
for(int j = 1; j <= n; j++)//删除头不符合条件的
for(int x = 1; x <= n; x++)//from
{
if(j == x)
continue;
while(!q[j][x].empty()
&& i - l > q[j][x].front())
q[j][x].pop_front();
}

for(int j = 1; j <= n; j++)//单独考虑j
for(int x = 1; x <= n; x++)//from 从什么方向转移
{
if(j == x)
continue;
int v = 0;
if(!q[j][x].empty())
v = q[j][x].front();
dp[i][j] = min(dp[v][x] - sum[v][j] + sum[i][j] + cost, dp[i][j]);
}
for(int j = 1; j <= n; j++)//删除尾不单调(不最优的)
for(int x = 1; x <= n; x++)//from
{
if(j == x)
continue;
while(!q[j][x].empty()
&& dp[i][x] - sum[i][j] <=
dp[q[j][x].back()][x] - sum[q[j][x].back()][j])
q[j][x].pop_back();
q[j][x].push_back(i);
}
}
int ans = INF;
for(int i = 1; i <= n; i++)
ans = min(ans, dp[m][i]);
printf("%d\n", ans - cost);
}
return 0;
}