二分搜索 小讲 【 理解 + 例题 】 更新ing......
2014-10-18 11:25
169 查看
人生路漫漫、、、、
今天我们来讲一下二分搜索,在刚开始学C语言的时候,就有这样的一段代码,是关于二分查找的,直到大一暑假集训的时候,才知道,二分法可以解决那么多的问题,现在,算是回顾,也算是更深的一步理解吧、 分三种类型来讲一讲、、、
该题就是不断地去寻找最长的,符合题意的绳子的长度
POJ 2456 Aggressive cows 链接:http://poj.org/problem?id=2456
此题中是要最大化最近的两头牛之间的距离。
题目: 有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 ) 的值进行排序贪心地进行选取即可。
今天我们来讲一下二分搜索,在刚开始学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"); } }
相关文章推荐
- 树状DP 小讲 【 理解 + 例题 】 更新 ing......
- 最短路 - floyd 【 理解 + 例题 】 更新 ing...
- KMP小讲 【理解 + 例题】 更新 ing.....
- 鸽笼原理 小讲 【 理解 + 例题 】 更新 ing ...
- 网络流 - Edmond-Karp 小讲 【 理解 + 例题 】 更新 ing...
- 最短路 - spfa - (二) 【 理解 + 例题 】 更新 ing......
- 最小生成树 - prim 小讲 【 理解 + 例题 】 更新 ing...
- 逆序数 小讲 【 理解 + 例题 】 更新ing....
- 快速排序 小讲 - (二)【 理解 + 例题 】 更新 ing...
- 数位DP 小讲 【 理解 + 例题 】 更新 ing......
- 欧几里德与扩展欧几里德 小讲 【 理解 + 例题 】 更新 ing
- 二叉排序树(BST) 小讲 【 理解 + 例题 】 更新ing ...
- 二分搜索问题(未理解)
- 二分搜索及其思想(挑战例题总结)
- stirling数 小讲 【 理解 + 例题 】 ing...
- 【 题集 】 【kuangbin带你飞】专题一 简单搜索 更新 ing ......
- 用google工具栏搜索结果转到百度?(更新,使用OpenDNS已解决)
- 二分搜索
- YetAnotherForum.Net+ScrewTurn Wiki中文完美汉化增强版(07-16更新:升级到v1.2.3 新增显示论坛版主信息,修复Wiki中文搜索)(GPL协议源码发布)
- 自己一些对Exchange 2007 Autodiscover服务的新理解。。。。更新中