[HNOI2001]产品加工
洛谷题面
清新 \(\rm DP\) 题。
题目大意
有 \(n\) 个任务,第 \(i\) 个任务可以给 A 机器做,用时 \(t_1[i]\);给 B 机器做,用时 \(t_2[i]\);给 A 机器和 B 机器同时做,用时 \(t_3[i]\)。
求最小用时。
当对应 \(t[i]=0\) 时,代表不能给这个机器或这两个机器做。
题目分析
令 \(dp[i][j][k]\) 表示完成了前 \(i\) 个任务,第一台机器耗时为 \(j\),另一台耗时为 \(k\),此时的最小总用时。
最终答案即为 \(\min\{dp[n][j][k]\}\)。
先不论时间,即便用上滚动数组优化,空间复杂度也已经炸成花了,我们考虑一个巧妙的优化:
令 \(dp[i][j]\) 表示完成了前 \(i\) 个 任务,第一台机器耗时为 \(j\) 时第二台机器的最小耗时。
直接将 【第二台机器耗时】 这个东西干掉了!(
本题中状态转移方程并非重点,但还是摆上来:
\(dp[i][j]=\min\{dp[i-1][j]+t2[i],dp[i-1][j-t1[i]],dp[i-1][j-t3[i]]+t3[i]\}\)
仍然是可以滚动优化的。
时间复杂度 \(\mathcal{O}(nk)\),空间复杂度 \(\mathcal{O}(ns)\)。
\(s\) 是 \(t1,t2,t3\) 的取值范围,\(k\) 为 \(\sum_{i=1}^n \max\{t1[i],t2[i],t3[i]\}\) 故 \(k\) 最大为 \(6\times 10^3\times5=3\times 10^4\)。
可以通过本题。
代码
//2022/1/8
const int INF=0x3f3f3f3f;
const int ma1=6005,ma2=30005;
int t1[ma1],t2[ma1],t3[ma1];
int dp[2][ma2];
int n;
int main(void)
{
n=read();
mst(dp,0x3f);
dp[0][0]=0;
for(register int i=1;i<=n;i++)
{
t1[i]=read(),t2[i]=read(),t3[i]=read();
}
int sum(0);
for(register int i=1;i<=n;i++)
{
mst(dp[i&1],0x3f);
sum+=max({t1[i],t2[i],t3[i]});
for(register int j=0;j<=sum;j++)
{
if(t2[i]!=0)
{
dp[i&1][j]=min(dp[i&1][j],dp[i&1^1][j]+t2[i]);
}
if(t1[i]!=0 && j>=t1[i])
{
dp[i&1][j]=min(dp[i&1][j],dp[i&1^1][j-t1[i]]);
}
if(t3[i]!=0 && j>=t3[i])
{
dp[i&1][j]=min(dp[i&1][j],dp[i&1^1][j-t3[i]]+t3[i]);
}
}
}
int ans=INF;
for(register int i=0;i<=sum;i++)
{
ans=min(ans,max(i,dp[n&1][i]));
}
printf("%d\n",ans);
return 0;
}