题解 P2224 [HNOI2001]产品加工

一道很有趣的 dp 题。

这道题是以答案为下标来设定状态,在这种生产问题这个套路还是挺常见的,需要积累一下。

我们令 \(f_{i,j}\) 为前 \(i\) 个任务 \(A\) 机器花了\(j\) 时间的时候,\(B\) 机器花费的时间最少是多少。

所以答案就是 \(\min\{\max\{i,f_{n,i}\}\}\) 。我们对于每一个时间 \(i\) 所需要的总时间就是做完前 \(n\) 个任务 \(B\) 花费的时间和 \(A\) 花费的时间的最大值。由于我们枚举的 \(i\) 就是 \(A\) 花费的时间,所以只需与 \(f_{n,i}\) 取最大值即可。

转移可以考虑从三个方向转移过来,对于当前任务,我们分别考虑是 \(A\) 做,\(B\) 做,还是 \(A\)\(B\) 一起做。

即可以推出转移方程。

\[f_{i,j}=\max\left\{\begin{array}{l}f_{i-1,j-t1_i}\\ f_{i-1,j}+t2_i\\ f_{i-1,j-t3_i}+t3_i \end{array}\right. \]

我们发现这样的空间开销是很大的,因为我们既要记录是哪个任务还要记录时间。

我们发现对于第 \(i\) 个任务只能从 \(i-1\) 这个任务转移过来,所以可以滚动数组。

还有一个关于枚举的时间的优化。不难发现,对于当前任务 \(i\) 的时间 \(j\)\(j\) 一定不会超过前 \(i-1\) 个任务所花的时间的最大值。

\(j\le \sum{\max(t1_i,t2_i,t3_i)}\)

细节不算多,但注意数组开大点,因为时间的大小比较玄学。


#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e4+10;
int t1[N],t2[N],t3[N];
int n;
int dp[10][N];
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>t1[i]>>t2[i]>>t3[i];
	}
	int sum=0;
	memset(dp,0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		sum+=max(t1[i],t3[i]);
		memset(dp[i&1],0x3f,sizeof(dp[i&1]));
		for(int j=0;j<=sum;j++)
		{
			if(t2[i]!=0) dp[i&1][j]=min(dp[(i&1)^1][j]+t2[i],dp[i&1][j]);
			if(t1[i]!=0 and j-t1[i]>=0) dp[i&1][j]=min(dp[i&1][j],dp[(i&1)^1][j-t1[i]]);
			if(t3[i]!=0 and j-t3[i]>=0) dp[i&1][j]=min(dp[i&1][j],dp[(i&1)^1][j-t3[i]]+t3[i]);
		}
	}
	int ans=0x7fffffff;
	for(int i=0;i<=sum;i++)
	{
		ans=min(ans,max(i,dp[n&1][i]));
	}
	cout<<ans;
	return 0;
}
posted @ 2022-10-20 11:49  zplqwq  阅读(23)  评论(0编辑  收藏  举报