Rhythm Game_agc072分析与解答

先把题目转化为一个调度问题:去踩一个按钮要从原点出发,按下按钮,回到原点,将这个过程视为执行(完成)一个任务,这样,每个任务有一个释放时间,必须在这个时间点或其后开始这个任务才可以,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;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值