是道好题,卡常卡到我裂开/kk
Solution [HNOI2001]产品加工
题目大意:有两台机器,共(n)个任务,每个任务你可以让第一台机器单独做花费它(a)时间,可以让第二台机器单独做花费它(b)时间,可以让两台机器同时做都花费(c)时间(部分任务有特殊要求,只能某些机器做),求完成所有任务最小时间
动态规划,奇奇怪怪的优化
分析:
首先题意有个不明确的地方(也可能是我没有读出来),就是时间(c)代表两台机器是分工做,而不是同时做,它们互不影响,也就是如果两台机器要共同完成一个任务,并不需要等到两台机器都空闲了,才能一起做
然后我们发现,当前的状态可以用一个三元组表示出来,((i,a,b))表示已经做了前(i)个任务,第一台机器花费(a)时间,第二台机器花费(b)时间,总时间就是(max(a,b)),三元组的转移十分简单就不在此列出
空间不允许我们开三维数组,我们可以开二维,把(i,a)丢给下标表示,用数组值来表示(c),因为我们要求最小值,所以只需保存一个(c)最小的三元组即可
那么(f[i][a])实际上表示已经做了前(i)个任务,第一台机器花费(a)时间的时候第二台机器最少花费的时间
转移方程:
(t_i)表示第(i)个任务,(t_i.a)表示给第一个机器做的时间(其他两个同理),转移条件略去qaq(主要是懒得打)
这个朴素(dp)是跑不过去的,需要亿点点优化
-
1.滚动数组消去第一维,使空间消耗变得可以接受,并且对连续空间的内存访问可以利用Cache大幅优化常数
-
2.枚举上下界优化,我们记录一个最小的下界(down),表示(down)是最小的使(f[i][down])状态存在的值。记录一个上界(up),显然(up=sum_{j=1}^i max(t_j.a,t_j.b,t_j.c))
-
3.边读入边计算,原理同1
附上不开O2可以勉强卡过的代码:
#include <cstdio>
#include <cstring>
#include <cctype>
using namespace std;
const int maxn = 6033,maxm = 33333;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
inline int min(int a,int b){return a < b ? a : b;}
inline int max(int a,int b){return a > b ? a : b;}
unsigned short f[maxm];
int n,ans = 0x7fffffff,down,up;
int main(){
n = read();
for(register int i = 1;i <= n;i++){
int a = read(),b = read(),c = read(),beg = down;
down = 0x7fffffff,up += max(a,max(b,c));
for(register int j = up;j >= beg;j--){
int tmp = 0x3f3f3f3f;
if(a && j - a >= beg)tmp = min(tmp,f[j - a]);
if(b)tmp = min(tmp,f[j] + b);
if(c && j - c >= beg)tmp = min(tmp,f[j - c] + c);
if(tmp < 0x3f3f3f3f)down = min(down,j);
f[j] = tmp;
}
}
for(int i = down;i <= up;i++)ans = min(ans,max(i,f[i]));
printf("%d
",ans);
return 0;
}