【ybtoj】二分算法-最小时间

最小时间

题目描述

有n个物品,第i个物品有两个属性k,b,表示它在时刻 的价值为k*x+b。
当前处于时刻0,你可以选择不超过m个物品,使得存在非负整数时刻t,你选择的所有物品的总价值大于等于S。
给出S,求t的最小值。

输入格式

第一行三个整数n,m,S。
接下来n行,第i行两个整数k,b。

输出格式

一行一个整数表示答案。

样例 1 输入

  3 2 100
  3 9
  -2 50
  4 1

样例 1 输出

13

样例 1 解释:

选择1,3号物品。

样例 2 输入

  3 2 100
  -1 49
  -2 50
  1 -998244353

样例 2 输出

998244453

样例 2 解释:

选择3号物品。

分析

选择任何一个集合,得到的收益和都可以表示为一个一次函数的形式.我们只关心这些一次函数的最大值,可以发现这个最大值一定是先降后升的(也有可能是单调递增或单调递减).
因此我们只需要check一下0时刻是否符合条件,如果不符合则进行二分.
注意check的时候我们只需要找出最大的m个即可,因此可以O(n)地做.具体做法是快排的时候只递归一边,当然你不需要手写,直接用STL的nth_element()即可

Code:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
long long k[1000086],b[1000086],h[1000086];
bool cmp(long long x,long long y){//cmp排序
	return x>y;
}	
	long long l=0,r=1e9;
	long long mid;
	long long sum=0;
	long long ans=0;
int main(){
	long long n,m,s;
	cin>>n>>m>>s;
	for (int i=1;i<=n;i++){
		cin>>k[i]>>b[i];
	}

	while(l<r){
		mid=(l+r)/2;
		sum=0;
		for (int i=1;i<=n;i++){
			h[i]=k[i]*mid+b[i];
		}
		
		nth_element(h+1,h+m,h+1+n,cmp);
		for (int i=1;i<=m;i++){
			if(h[i]<0)continue;
			sum+=h[i];
			if(sum>=s){
				ans=mid;
				r=mid;
				goto next; 
			}
		}
		l=mid+1;
		next: ;
	}
	cout<<ans;
	return 0;
}
```

```
</details>

所谓的nth_element()作用就是是给定位置的值成为这个位置该有的值.

以后我会单独出一篇专门介绍nth_element文章
供各位学习与交流
posted @ 2022-02-18 15:12  su-yichen  阅读(112)  评论(0编辑  收藏  举报