Solving the Problems
Time Limit:1000MS Memory Limit:65536K
Total Submit:36 Accepted:13
Description
Programming is fun, Aaron is addicted to it. In order to improve his programming skill, he decides to solve one programming problem per day. As you know, different problems have different properties, some problems are so difficult that there are few people can solve it, while some problems are so easy that almost everyone is able to tackle it.
Programming skill can be measured by an integer p. And all problems are described by two integers ai and bi. ai indicates that if and only if P >= ai, you can solve this problem. bi indicates that after you solve this problem, your programming skill can be increased by bi.
Given the initial programming skill p of Aaron, and the information of each problem, Aaron want to know the maximal programming skill he can reach after m days, can you help him?
Input
Input consists of multiple test cases (less than 40 cases)!
For each test case, the first line contains three numbers: n, m, p (1 <= n <= 100000, 1 <= m <= n, 1 <= p <= 10000), n is the number of problems available for Aaron, m, p as mentioned above.
The following n lines each contain two numbers: ai and bi (1 <= ai <= 10000, 1 <= bi <= 10000) describe the information of the i-th problem as memtioned above.
There's a blank line between consecutive cases.
Output
For each case, output the maximal programming skill Aaron can reach after m days in a line.
Sample Input
2 2 1 1 2 7 3 3 1 2 1 2 2 3 3 4
Sample Output
3 5
题目大意:
Aaron的编程能力为p,他想用m天时间每天做一题来提高自己的编程能
力,对于每道题目,都又做这题的最小能力要求,达到这个能力ai后才能
做,做了这题后编程能力会提升bi(就像砍小怪,砍掉以后会获得经验值,
就能升级^_^)问m天后Aaron的编程能力最多能提升到多少。
解题思路:
为了使m天编程能力提升最多,那么每天要提升尽可能多,就是做自己
力所能及的题目中能使自己编程能力提升最多的(bi最大的)。
那么就能想到一个朴素的算法:
1.对题目按ai从小到大排序
2.每次从小到大扫描题目,选出ai<=p中bi最大的一道题
3.把这道题删掉
4.重复2,3直到m次后
但是...一分析时间复杂度就知道这样不行,这样每次扫描花费n,
要扫描m次,复杂度O(n*m),n,m<=100000,最后情况下
10^10,显然要超时的。
但发现p是递增的,所以前一天能做的题目,后一天肯定能做,因为题目
对能力的要求没变而实力增强了。
这时能想道一个高级的算法:
1.对题目按ai从小到大排序,指针k指向第一题
2.从小到大扫描题目,把题目加入容器v,直到ai>p,k指向i
3.取出容器中的最大值加到p上去,把这个题删掉
4.重复2,3
这样对所有题目只扫描了一遍,而从容器中取出最大值(记为操作q)执
行了m次。所以时间复杂度为m*o(q);
所以只要q的时间负责度降下来,这道题目就搞定了。
而对于操作q,可以用堆和排序二叉树来实现,时间负责度o(logn);
但问题是堆和排序二叉树都不好写,编程复杂度高又容易出错。
但是STL给了我们希望,里面的set容器和优先队列(priority_queue)完
美解决了这个问题,set容器是
用平衡二叉树实现的,优先队列用堆实现,时间负责度也是o(logn),
set默认从小到大排序,优先队列默认从大到小排序,所在这里我选择了优
先队列。
优先队列操作:
q.push():压入一个数据
q.top():返回最顶层的那个数据,不删除
q.pop():删除最顶层的数据
q.size():返回容器里元素的个数,0表示队列为空
q.empty():判断队列是否为空,空为true
总结:
核心算法:贪心,排序
数据结构:优先队列
时间复杂度:O(mlogn)
空间复杂度:O(n)
最后说下这题的注意事项,就是输入要用scanf(),不要用
cin,因为输入的数据量很大,用cin的话在输入过程中就
超时了,我就因为这个错误TLE了一下午!~@#$%*)+
具体看代码:
#include<iostream> #include<iterator> #include<queue> using namespace std; #define BG 2000000000 struct ss { int a,b; bool operator < (const ss &rhs) const { return (a<rhs.a); } }; priority_queue<int> q; int main() { int n,m,p,i,s,t,u; ss temp; ss a[100005]; //freopen("1685.in","r",stdin); while(scanf("%d%d%d",&n,&m,&p)!=EOF) { t=0; for(i=1;i<=n;i++) { scanf("%d%d",&temp.a,&temp.b); a[++t]=temp; } while (q.size()>0) q.pop(); sort(a,a+t); s=1; a[++t].a=BG;a[t].b=0; for(i=1;i<=m;i++) { for(u=s;u<=t;u++) if (a[u].a<=p) { q.push(a[u].b); } else {s=u;break;} if (u==t) if (a[u].a<=p) s=u+1; if (q.size()!=0) { p+=q.top(); q.pop(); } else break; } printf("%d/n",p); } return 0; }