P3619 魔法 题解

旅行传送门:快来点我鸭

题目描述
cjwssb知道是误会之后,跟你道了歉。你为了逗笑他,准备和他一起开始魔法。不过你的时间不多了,但是更惨的是你还需要完成n个魔法任务。假设你当前的时间为T,每个任务需要有一定的限制ti表示只有当你的T严格大于ti时你才能完成这个任务,完成任务并不需要消耗时间。当你完成第i个任务时,你的时间T会加上bi,此时要保证T在任何时刻都大于0,那么请问你是否能完成这n个魔法任务,如果可以,输出+1s,如果不行,输出-1s。

输入格式
第一行:一个整数Z,表示有Z个测试点。

对于每个测试点

第一行:一个整数n,T,表示有n个任务,你一开始有T的时间。

接下来n行,每行2个数字,ti与bi

输出格式
对于每个测试点,输出+1s或者-1s

输入输出样例
输入 #1复制
1
2 13
1 -9
5 -3
输出 #1复制
+1s
说明/提示
对于20%的数据,n≤10

对于100%的数据,n≤100,000,Z≤10,ti,T≤100,000,−100,000≤bi≤100,000

解题思路
(这题的算法标签应该加个暴力 )

初次审题,直觉告诉我们应该先选择bi较大的任务,因为bi越大,剩余的T就越大,能完成的任务就越多(就越有机会续1s ),若如此做,那你可以取得91分的好成绩。

那究竟是哪出问题了呢?以一组数据举例:

输入 复制
1
3 100
27 -19
91 -39
42 -5
输出 复制
+1s
而照我们之前的方法,这里的输出则为“-1”。此时此刻,相信细心的读者此时已经发现端倪了,按照之前的策略:我们先完成第三组任务,再完成第一组,最后再完成第二组,但第一组任务完成后的时间T = 76 < 91,续命失败;但我们换一种思路,先完成第三组,再完成第二组,最后再干第一组,结果与刚才恰恰相反。

那我们应该怎么做才能得到最优解呢?可以这样想:我们将每个任务的ti视为一个阈值,bi为完成任务后付出的代价,有的任务可能它付出的代价很大,但它的阈值却很低,而有的可能付出的代价很小,但阈值却高不可攀,因此如果我们一昧地追求付出的代价最小,可能就会错过一些本该可以完成的任务。像刚才的例子中,若是自作聪明地先完成了阈值和代价都较小的任务一,就会因为达不到任务二过高的阈值而得出错误的答案。

我们设第i个任务的阈值为ti,代价为bi,我们设第j个任务的阈值为tj,代价为bj,若出现上述情况,我们可以得到下式:

T + bi > tj,
T + bj < ti.

化简得:ti + bi > tj + bj ,以此作为排序标准,完美收官。

AC代码

#include <bits/stdc++.h>
#define FOR(i, j, k) for (int i = (j); i <= (k); i++)
#define MAXN 100000 + 10

struct node
{
    int ti, bi;
} a[MAXN];

inline bool cmp(node a, node b)
{
    return a.ti + a.bi > b.ti + b.bi;
}

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

int main(int argc, char const *argv[])
{
    int z = read();
    while (z--)
    {
        int n = read(), t = read(), flag = 0;
        FOR(i, 1, n)
        a[i].ti = read(), a[i].bi = read();
        std::sort(a + 1, a + n + 1, cmp);
        FOR(i, 1, n)
        {
            if (t > a[i].ti)
                t += a[i].bi;
            else
            {
                flag = 1;
                break;
            }
            //保证T在任何时刻都大于0.
            if (t <= 0)
            {
                flag = 1;
                break;
            }
        }
        if (flag)
            printf("-1s\n");
        else
            printf("+1s\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值