UVALive 3507:Keep the Customer Satisfied(贪心 Grade C)

VJ题目链接

题意:

知道n(n <= 8e6)个工作的完成所需时间q和截止时间d,你一次只能做一个工作。问最多能做多少工作?

思路:

首先很像贪心。观察发现如下两个贪心性质:

1)一定存在一个最优方案,使得截止时间靠后的工作一定比截止时间靠前的工作迟完成(如果完成的话)

证明:

若工作i, j 有 d[i] > d[j]。假设我们现在有一个工作方案,使得i工作在j工作之前完成。

如  ..., i , ... , j , ...

记做 preI, i, midIJ, j, afterJ

此时将这个工作方案的i移动到j后面一个完成,其他不动。

则变成 preI , midIJ , j , i , afterJ

如此交换对 preI和afterJ 部分不会有影响。

对于midIJ部分,由于i的抽出,使得整体完成时间前移,所以不会导致结果更差。

对于j,原先在截止时间内,现在抽出了i,所以一定也在截止时间内。

对于i,因为d[i] > d[j],而现在i的完成时间等同于原先j的完成时间,所以i也一定能完成。

所以这个交换方案不会使得结果更差。

至于会不会变得更好,没有证明,感觉会吧~~迟一点的可以拖一下嘛。。。

2)若存在一种方案,使得 d[i] < d[j] 且 q[i] > q[j] 且选择了i而没有选择j, 则一定可以用j代替i。

证明:略。

所以采用如下贪心策略:

按d从小到大排序,然后依次放入。若放入后爆炸,则看一下前面最大的那个,如果最大的比这个大,踹走后,把这个放进去。

【注:本题证明不完备。可能有问题。如有发现,多谢指正】

坑点:忘记两组数据之间有空行。。。

代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

#define N 800100

struct Work{
    int q, d;
    void read() {
        scanf("%d%d", &q, &d);
    }
    bool operator < (const Work &b) const {
        if (d != b.d) return d < b.d;
        return q < b.q;
    }
}works[N];

priority_queue<int> que;

int main() {
    int t;
    scanf("%d", &t);
    int _ = 0;
    while (t--) {
        !_++?:puts("");
        int n;
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            works[i].read();
        }

        sort(works, works+n);

        while (!que.empty()) que.pop();
        int now = 0;
        int cnt = 0;
        for (int i = 0; i < n; i++) {
            if (now + works[i].q <= works[i].d) {
                now += works[i].q;
                que.push(works[i].q);
                cnt++;
            } else {
                if (!que.empty() && works[i].q < que.top()) {
                    now -= que.top();
                    now += works[i].q;
                    que.pop();
                    que.push(works[i].q);
                }
            }
        }
        printf("%d
", cnt);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shinecheng/p/4000461.html