LA3971 Assemble
你有b块钱,给出n个配件的个子的种类,性能和价格,每种类型的配件买一个,价格不超过b,因为有水桶效应,所以电脑的性能取决于性能最低的配件的性能,问你b块钱配的电脑性能最高有多少。
按照白书的说法,最大值尽量小,最小值尽量大之类的问题一般都可以用二分答案的方法来结局,这道题就是一道典型的最小值最大问题,所以采用二分答案
#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
#include <map>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
const int maxn = 1005;
struct Component {
int price,quality;
};
int cnt,budget,maxquality;
map<string,int> id;
vector<Component> comp[maxn];
void readin() {
char name[30],power[30];
int p,q,n;
cnt = maxquality = 0;
scanf("%d%d\n",&n,&budget);
id.clear();
for(int i = 0;comp[i].size();i++) {
comp[i].clear();
}
for(int i = 0;i < n;i++) {
scanf("%s%s%d%d",name,power,&p,&q);
int nowid;
if(id.count(name)) {
nowid = id[name];
} else {
nowid = cnt++;
id[name] = nowid;
}
comp[nowid].push_back((Component){p,q});
if(q > maxquality) {
maxquality = q;
}
}
}
bool ok(int val) {
int total = 0;
for(int i = 0;i < cnt;i++){
int cheapest = INT_MAX,m = comp[i].size();
for(int j = 0;j < m;j++) if(comp[i][j].quality >= val) {
cheapest = min(cheapest,comp[i][j].price);
}
if(cheapest == INT_MAX) return false;
total += cheapest;
if(total > budget) {
return false;
}
}
return true;
}
void work() {
int str = 0,end = maxquality,mid;
while(str < end) {
mid = str + (end - str + 1) / 2;
if(ok(mid)) {
str = mid;
} else {
end = mid - 1;
}
}
cout << str << endl;
}
int main() {
int T; cin >> T;
while(T--) {
readin();
work();
}
return 0;
}
LA3635
你请来了F个朋友,一起来分N个圆形的派,每个人得到的必须是一块或者是一块派的一部分,而不是几块派拼在一起的,问你每个人最多能得到多大的派
对浮点数的二分,设一个eps,当end-str<eps的时候跳出循环
PS.常量π的值可以用const int PI = acos(-1.0)来得,这样精度比较靠谱
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 10005;
const double PI = acos(-1.0);
const double eps = 1e-5;
int R[maxn];
inline double square(int R) {
return PI * R * R;
}
int main() {
int T,N,F;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&N,&F);
int maxR = 0;
for(int i = 0;i < N;i++) {
scanf("%d",&R[i]);
if(R[i] > maxR) {
maxR = R[i];
}
}
double str = 0,end = square(maxR);
while(end - str > eps) {
double mid = (str + end) / 2;
int count = 0;
for(int i = 0;i < N;i++) {
count += (int)(square(R[i]) / mid);
if(count >= F + 1) {
break;
}
}
if(count >= F + 1) {
str = mid;
} else {
end = mid;
}
}
printf("%.4lf\n",str);
}
return 0;
}
LA 3177 Beijing Guards
有n个人围城一个圈,每个人想拿ri种物品,要求相邻的两个人不能拿一样的,问最少需要多少种物品
当n为偶数的时候非常好处理,只要找到最大的ri+ri-1的和就行了。
当n为奇数的时候可以对所需要的物品数进行二分。
第一个人需要r1件物品,设left[i],right[i]分别为第i个人从1~ri,ri+1~p中拿的物品数目,偶数的人尽量拿前面的,奇数的人尽量拿后面的,那么第n个人必定是尽量拿后面的,此时判断时候和r1冲突即可
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100001;
int r[maxn],n,sum,left[maxn],right[maxn];
bool ok(int val) {
right[0] = 0; left[0] = r[0];
for(int i = 1;i < n;i++) {
if(r[i] > val - left[i - 1] - right[i - 1]) return false;
if((i + 1) % 2 == 0) {
left[i] = min(r[i],r[0] - left[i - 1]);
right[i] = r[i] - left[i];
} else {
right[i] = min(r[i],val - r[0] - right[i - 1]);
left[i] = r[i] - right[i];
}
}
return left[n - 1] == 0;
}
int bsearch() {
int L = 1,R = sum;
while(L < R) {
int mid = (L + R) / 2;
if(ok(mid)) R = mid;
else L = mid + 1;
}
return L;
}
int main() {
while(scanf("%d",&n),n) {
sum = 0;
for(int i = 0;i < n;i++) {
scanf("%d",&r[i]);
sum += r[i];
}
if(n % 2 == 1) printf("%d\n",bsearch());
else {
int maxd = 0;
for(int i = 0;i < n;i++) {
int f = ((i == 0) ? n - 1 : i - 1);
maxd = max(maxd,r[i] + r[f]);
}
printf("%d\n",maxd);
}
}
return 0;
}
关于二分查找的一些总结:
很多问题都可以用二分来解决,但是根据每次寻找的条件不同,迭代的时候值的变化也会不同,简单总结一下;
1、查找某个元素是否存在
mid = (begin + end) / 2
if(mid < v) begin = mid + 1;
if(mid > v) end = mid – 1;
if(mid == v) return v;
2、查找不小于v的最小值
mid = (end + begin) / 2
if(mid < v) begin = mid + 1; else end = mid;
3、查找大于v的最小值
mid = (end + begin) / 2
if(mid <= v) begin = mid + 1; else end = mid;
3、查找满足条件的v的最小值
mid = (begin + end) / 2
if(ok(mid)) end = mid; else begin = mid – 1;
4、查找满足条件的v的最大值
mid = begin + (end – begin +1) / 2
if(ok(mid)) begin = mid; else end = mid – 1;
可以发现在寻找满足条件的v的最大值的时候mid取值和其他时候不一样,因为在处理两个相邻的数字的时候优先尝试大的那个,mid这样取可以保证一定比begin大,不然有可能会进入死循环
这里顺便总结一下STL中lower_bound和upper_bound 的用法
原型如下
template <class ForwardIterator, class T>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val);
template <class ForwardIterator, class T, class Compare>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val, Compare comp);
template <class ForwardIterator, class T>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,const T& val);
template <class ForwardIterator, class T, class Compare>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,const T& val, Compare comp);
函数有两种重载,区别就是多了一个最后的比较函数,默认使用类的operator<,也可以自定义cmp函数,函数接受两个迭代器,分别表示一段范围,注意last表示的是最后一个元素的后一个,返回一个指向所需元素的迭代器,失败了返回last