UVALive 3507:Keep the Customer Satisfied(贪心 Grade C)
题意:
知道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\n", cnt); } return 0; }
posted on 2014-09-29 16:12 ShineCheng 阅读(198) 评论(0) 编辑 收藏 举报