先把题目转化为一个调度问题:去踩一个按钮要从原点出发,按下按钮,回到原点,将这个过程视为执行(完成)一个任务,这样,每个任务有一个释放时间,必须在这个时间点或其后开始这个任务才可以,i号任务的释放时间是:T_i-X_i,减去X_i因为从原点到X_i的位置需要X_i时间,然后一旦开始这个任务,这个任务所花费的时间是2X_i,除此之外,由于按钮只持续D+0.5秒,这个任务还有一个最晚结束时间,是(释放时间+经过时间+D+0.5)=T_i+X_i+D+0.5,由于移动速度是1,所以在整数坐标点上时的时刻一定也是整数,最晚结束时间指的是回到原点时的时刻,原点是整数点,所以最晚结束时间中的0.5可以忽略。
经过以上分析,每个任务有:
释放时间(release time) T_i-X_i
经过时间 (cost) :2X_i
最晚完成时间(deadline aka. ddl):T_i+X_i+D
如果没有release time,也就是不用等待一个任务被释放就可以直接去执行这个任务,那么按照ddl从小到大的顺序执行任务最优
如果有release time应该如何安排任务执行顺序呢?这里有一个关键观察:如果先执行了某个ddl靠后的任务,则ddl靠前的任务一定被释放,证明:执行完ddl靠后的任务 i 的最早时间是T_i+X_i,而ddl靠前的任务 j 的释放时间是T_ j - X_ j <= T_j + X_ j <=T_ i + X_ i
当完成一个任务 i 后,比i的ddl靠前的任务都被释放了,除此之外的是比i的ddl晚的任务,顺利完成任务也就是在所有任务的ddl前分别完成他们,那么应该先去执行比 i 的ddl靠前的任务 j1,j2,j3...
执行j1,j2,j3..时,谁的ddl靠前谁先执行。
将所有任务按照ddl从小到大排序,则执行的任务编号形成这样的结构:
当完成任务 j 后,从j之后的任务中选一个任务 i 执行,执行完 i 后,分别执行 j+1,j+2,,,i-1,之后任务1到任务 i 执行完成
这个结构启示我们用dp解决,定义dp[i]为完成第1个到第n个任务的最早时间
之后的dp内容,参考:Editorial - AtCoder Grand Contest 072
#include<bits/stdc++.h>
using namespace std;
using ll=long long ;
const ll maxn=5000+5,inf=1e15;
struct Task{
ll t,x;
bool operator < (const Task &rhs) const {
return t+x<rhs.t+rhs.x;
}
}task[maxn];
ll d,n;
ll dp[maxn];
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
freopen("D:/in.txt","r",stdin);
ll T;cin>>T;
while(T--){
cin>>n>>d;
for(ll i=1;i<=n;i++) cin>>task[i].t>>task[i].x;
stable_sort(task+1,task+1+n);
for(ll i=0;i<=n;i++) dp[i]=inf;
dp[0]=0;
for(ll j=0;j<=n;j++){
ll sum=0,ma=-inf;
for(ll i=j+1;i<=n;i++){
ll t0=max(dp[j],task[i].t-task[i].x)+2*task[i].x;
if(t0<=task[i].t+task[i].x+d){
if(ma<=d-t0){
dp[i]=min(dp[i],t0+sum*2);
}
}
sum+=task[i].x;
ma=max(ma,2*sum-task[i].t-task[i].x);
}
}
if(dp[n]<inf) cout<<"Yes"<<"\n";
else cout<<"No"<<"\n";
}
return 0;
}
639

被折叠的 条评论
为什么被折叠?



