人生路漫漫、、、、
今天我们来讲一下二分搜索,在刚开始学C语言的时候,就有这样的一段代码,是关于二分查找的,直到大一暑假集训的时候,才知道,二分法可以解决那么多的问题,现在,算是回顾,也算是更深的一步理解吧、 分三种类型来讲一讲、、、
1、 判断一个解是否可行
题目 :POJ 1064 Cable master 链接:http://poj.org/problem?id=1064
该题就是不断地去寻找最长的,符合题意的绳子的长度
#include<stdio.h>
#include<algorithm>
#include<cmath>
using namespace std;
double tt[10010];
int main()
{
int n, m;
while(~scanf("%d%d",&n,&m))
{
double r = 0, l = 1.0;
if(n == 0 && m == 0)
break;
for(int i = 0; i < n; i ++)
{
scanf("%lf",&tt[i]);
r = max(tt[i], r);
}
while(r - l > 1e-6)
{
double mid = (l + r ) / 2;
int cnt = 0;
for(int i = 0; i < n ; i ++)
{
cnt += floor(tt[i] / mid);
}
if(cnt >= m)
l = mid;
else
r = mid;
}
printf("%.2f\n", l); // 这里要注意,使用%2.lf 是WA 的,不知为啥、
}
}
2、 最小值最大化
最小值最大化,抑或是最大值最小化,都可以用二分搜索来解决,但是要注意的是构建这样的一个模型、、
POJ 2456 Aggressive cows 链接:http://poj.org/problem?id=2456
此题中是要最大化最近的两头牛之间的距离。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int tt[200010];
int i, j;
int n, m;
bool judge(int k)
{
int i,max = tt[0], sum = 0;
for(i = 1; i < n; i ++)
{
if(tt[i] - max >= k)
{
sum ++;
max = tt[i];
}
if(sum >= m - 1)
return true;
}
return false;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(i = 0; i < n ; i ++)
scanf("%d",&tt[i]);
sort(tt, tt + n);
int maxn = tt[n - 1] - tt[0];
int l = 0;
int tmp = - 1;
int mid;
while(l <= maxn)
{
mid = (maxn + l) >> 1;
if(judge(mid))
l = mid + 1;
else
maxn = mid - 1;
}
printf("%d\n", l - 1);
}
return 0;
}
3、 最大化平均值
这个我看了好久,然后也推了好久的公式,后来发现,原来这么地简单 、、、
题目: 有n 个物品的重量和价值分别是wi 和vi ,从中间选出k 个物品使得单位重量的价值最大。
1 <= k <= n <= 10^4 , 1 <= wi ,vi <= 10^6
INPUT :
n = 3, k = 2;
{w, v} = { {2, 2} , {5, 3} , {2, 1} };
OUTPUT :
0.75 (选1 号 和 3 号, (2 + 1)/ (2 + 2) = 0.75 )
刚开始拿到这题的时候,就以为是贪心,后来发现,这不是 啊!!
我们可以这样想,题目是要我们求sum(vi)/ sum (wi) 的最大值, 我们用二分搜索的时候,就是要使sum(vi)/ sum (wi) 尽可能的大,假设存在这样的一个x,使得
sum(vi)/ sum (wi) >= x ,并且满足题意的情况下,经过变形,就有这样的公式:只要满足 sum(vi - x * wi ) >= 0 即可,所以,剩下来的,只需要对(vi - x * wi ) 的值进行排序贪心地进行选取即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 10010
int n, k;
int w[MAXN], v[MAXN];
double xx[MAXN];
bool judge(double x)
{
for(int i = 0; i < n ; i ++)
{
xx[i] = v[i] - x * w[i];
}
sort(xx, xx + n);
double sum = 0;
for(int i = 0; i < k; i ++) // 贪心的一个过程
{
sum += xx[n - i - 1];
}
return sum >= 0;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
double l = 0, maxn = 0;
for(int i = 0; i < n; i ++)
{
scanf("%d%d",&w[i], &v[i]);
maxn = max(maxn, v[i] *1.0/ w[i]); //我加的这个,算是优化么-.-
}
while(maxn - l >= 1e-6)
{
double mid = (l + maxn) / 2;
if(judge(mid))
l = mid;
else
maxn = mid;
printf("%.2f%.2f\n", l, maxn);
}
printf("%.2f\n",maxn );
}
}
/*
3 2
2 2
5 3
2 1
*/
今天突然做到这题,POJ 3111 K Best 链接:http://poj.org/problem?id=3111
几乎和上面一样的思路,要求一定要取哪个,才能获得最大值,我的思路是这样的,先求出最大值,再用去判断,是否满足,即取出 公式值最大的前k 个数据即可,我这里用的是结构体来存顺序。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 100010
int n, k;
struct node
{
int w, v; // 宝石的质量和体积
int num; // 第num 个 宝石
double ans; //为了便于排序,所以放在结构体里面了
}ff[MAXN];
double xx[MAXN];
bool cmp(node a, node b)
{
if(a.ans - b.ans <= 1e-6)
return false;
else if(a.ans - b.ans > 1e-6)
return true;
return false;
}
bool judge(double x)
{
for(int i = 0; i < n ; i ++)
{
xx[i] = ff[i].v - x * ff[i].w;
}
sort(xx, xx + n);
double sum = 0;
for(int i = 0; i < k; i ++) // 贪心的一个过程
{
sum += xx[n - i - 1];
}
return sum >= 0;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
double l = 0, maxn = 0;
for(int i = 0; i < n; i ++)
{
scanf("%d%d",&ff[i].v, &ff[i].w);
maxn = max(maxn, ff[i].v *1.0/ ff[i].w);
ff[i].num = i + 1;
}
while(maxn - l >= 1e-6)
{
double mid = (l + maxn) / 2;
if(judge(mid))
l = mid;
else
maxn = mid;
// printf("%.2f %.2f\n", l, maxn);
}
//printf("%.2f\n",maxn );
for(int i = 0; i < n; i ++)
{
ff[i].ans = ff[i].v - ff[i].w * maxn;
}
sort(ff, ff + n, cmp);
printf("%d",ff[0].num);
for(int i = 1; i < k ; i ++) // 取出满足题意的前k 个即可
{
printf(" %d", ff[i].num);
}
printf("\n");
}
}