这是一道神奇的进程 DP,状态较难确定根据其他题解可知要以答案为状态
一、分析状态:
先看看有哪些状态:
- 执行第 i 个任务
- A 机器做的时间 j
- B 机器做的时间 k
没了
二、设计最简单的 DP 方案
考虑一个 naive 的暴力:
设三维 bool 数组 dp[i][j][k] 表示执行第 i 个任务时,A 已经做了 j 时,B 已经做了 k 时的状态是否存在
初始时除了 dp[0][0][0]=1,其他都是 0
显然有
dp[i][j][k]=dp[i−1][j−t1[i] ][k]ordp[i−1][j][k−t2[i] ]ordp[i−1][j−t3[i] ][k−t3[i] ]
其中 dp[i−1][j−t1[i] ][k] 表示单独交给 A,dp[i−1][j][k−t2[i] ] 表示单独交给 B,dp[i−1][j−t3][k−t3[i] ] 表示 AB 合作
复杂度已经 Θ(n3) 了,妥妥的 TMLE
三、降维优化
对上述 naive 暴力进行优化
大胆改造:dp[i][j]=k,即执行第 i 个任务时,A 已经做了 j 时,B 做的最小时间 k
一个很显然的贪心:A、B 都应当连续做,这样总时间才最短
初始值:
因为要求最小值,所以 dp[][]←+∞,又因为当执行第 0 个任务,A 做了 0 时,B 最小做 0 时,所以有 dp[0][0]←0
状态转移方程:dp[i][j]=min{dp[i−1][j−t1[i] ],dp[i−1][j]+t2[i],dp[i−1][j−t3[i] ]+t3[i]}
三项分别表示单独交给 A,单独交给 B,AB 合作,注意考虑是否能取到的问题,三项能取到的条件分别为 t1[i]=0,j>t1[i],t2[i]=0,t3[i]=0,j>t3[i]
最终答案即 i=1minmaxt{max{i,dp[n][i]}}
其中 maxt=i=1∑nmax{t1[i],t2[i],t3[i]}
四、时空优化
这样依然会 MLE,考虑使用滚动数组
因为 dp[i][] 仅由 dp[i−1][] 推得,所以可以压掉这一维
原式即为 dp[iand1][j]=min{dp[iand1xor1][j−t1[i] ],dp[iand1xor1][j]+t2[i],dp[iand1xor1][j−t3[i] ]+t3[i]},仍需考虑是否能取到
直接吃掉一维,但注意 dp 数组的第二维最大值得开到 n×t,即 3×104
细节比较多,所以...
五、代码
#include <bits/stdc++.h>
#define inl inline
#define rep(i,a,b) for(int i=(a),i##end=(b);i<=i##end;++i)
#define pre(i,a,b) for(int i=(a),i##end=(b);i>=i##end;--i)
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
using namespace std;
const int N=3e4+10;//注意数组大小
int dp[2][N],ans=0x3f3f3f3f,maxt;
signed main(void){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n;cin>>n;
memset(dp,0x3f,sizeof(dp));//初始化
dp[0][0]=0;
rep(i,1,n){
memset(dp[i&1],0x3f,sizeof(dp[i&1]));//注意这里要再初始化一次!
int x,y,z;cin>>x>>y>>z;maxt+=max(x,max(y,z));//省掉一个数组了
rep(j,0,maxt){
if(y)dp[i&1][j]=min(dp[i&1][j],dp[i&1^1][j]+y);
if(x&&j>=x)dp[i&1][j]=min(dp[i&1][j],dp[i&1^1][j-x]);
if(z&&j>=z)dp[i&1][j]=min(dp[i&1][j],dp[i&1^1][j-z]+z);//状态转移方程
}
}
rep(i,0,maxt)ans=min(ans,max(i,dp[n&1][i]));
cout<<ans;
}
六、总结
一道相当有趣且毒瘤的 DP 题,实现的思路非常诡异,而且还卡空间卡时(这里补充一句:如果开 O2 还没过的大常数选手可以考虑手写 max,min 函数以便卡常),需要运用滚动数组来进行空间优化