二分和三分
標簽(空格分隔): @zhshh cpp OI
二分問題
模板
離散二分答案
int check (int x){
//檢查x是否符合,符合 真,不符合 假
}
int search (int l,int r){
int mid;
while(l<=r){
mid=(l+r)/2;
if(check(mid)){
l=mid+1;
ans=mid;
}else{
r=mid-1;
}
}
return ans;
}
可以看到,假定一個\(x\)充分小,可以使得\(check(x)\)成立,那么明顯,在\(search(l,r)\)中,存在\(x_0\)使得\(x \in [l,x_0],check(x)=true\),\(x \in (x_0,r],check(x)=false\),注意\(x_0\)是從\(l\)開始最大的滿足的值。
也就是如果改成\(!check()\),那么就是\(check(x)=true\)是從\(x_{true}\)連續到r的,即\(x \in [l,x_0],!check(x)=true,x\in[x_{true},r],!check(x)=false\),因此二分求解的\(answer\)是\(x_0\),而最小成立值\(x_{true}=x_0+1\),因此返回時要return ans+1下面代碼。
int check (int x){
//檢查x是否符合,符合 真,不符合 假
}
int search (int l,int r){
int mid;
while(l<=r){
mid=(l+r)/2;
if(!check(mid)){
l=mid+1;
ans=mid;
}else{
r=mid-1;
}
}
return ans+1;
}
連續二分答案
很簡單的二分,邊界條件也不需要十分注意。最后return時,無論是left,right還是mid都可以,因為\(mid=(left+right)/2\)而此時mid,left,right,都是比較接近(最大差值eps)的。
#define eps 1e-6
int check (int x){
//檢查x是否符合,符合:真,不符合:假。
}
double search(){
double mid;
while(fabs(left-right)>eps){
mid=(left+right)/2;
if(check(mid)){
right=mid;
}else{
left=mid;
}
}
return left;
}
離散二分查找
轉載自你真的會寫二分查找嗎 By luoxn28
下面的都是針對直接sort后的不嚴格單調遞增的序列
下面六種本質區別就是一個恰當的check函數
1 查找第一個與key相等的元素
查找第一個相等的元素,也就是說等於查找key值的元素有好多個,返回這些元素最左邊的元素下標。
// 查找第一個相等的元素
static int findFirstEqual(int[] array, int key) {
int left = 0;
int right = array.length - 1;
// 這里必須是 <=
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] >= key) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
if (left < array.length && array[left] == key) {
return left;
}
return -1;
}
2 查找最后一個與key相等的元素
查找最后一個相等的元素,也就是說等於查找key值的元素有好多個,返回這些元素最右邊的元素下標。
// 查找最后一個相等的元素
static int findLastEqual(int[] array, int key) {
int left = 0;
int right = array.length - 1;
// 這里必須是 <=
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] <= key) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
if (right >= 0 && array[right] == key) {
return right;
}
return -1;
}
3 查找最后一個等於或者小於key的元素
查找最后一個等於或者小於key的元素,也就是說等於查找key值的元素有好多個,返回這些元素最右邊的元素下標;如果沒有等於key值的元素,則返回小於key的最右邊元素下標。
// 查找最后一個等於或者小於key的元素
static int findLastEqualSmaller(int[] array, int key) {
int left = 0;
int right = array.length - 1;
// 這里必須是 <=
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] > key) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
return right;
}
4 查找最后一個小於key的元素
查找最后一個小於key的元素,也就是說返回小於key的最右邊元素下標。
// 查找最后一個小於key的元素
static int findLastSmaller(int[] array, int key) {
int left = 0;
int right = array.length - 1;
// 這里必須是 <=
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] >= key) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
return right;
}
5 查找第一個等於或者大於key的元素
查找第一個等於或者大於key的元素,也就是說等於查找key值的元素有好多個,返回這些元素最左邊的元素下標;如果沒有等於key值的元素,則返回大於key的最左邊元素下標。
// 查找第一個等於或者大於key的元素
static int findFirstEqualLarger(int[] array, int key) {
int left = 0;
int right = array.length - 1;
// 這里必須是 <=
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] >= key) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
return left;
}
6 查找第一個大於key的元素
查找第一個等於key的元素,也就是說返回大於key的最左邊元素下標。
// 查找第一個大於key的元素
static int findFirstLarger(int[] array, int key) {
int left = 0;
int right = array.length - 1;
// 這里必須是 <=
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] > key) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
return left;
}
7 二分查找變種總結
#include <iostream>
#include <cstdio>
#define MX 10010
using namespace std;
int a[MX];
int n;
int FindFirstEqual(int key){
int left=1;
int right=n;
while(left<=right){
int mid=(left+right)/2;
if(a[mid]>=key){
right=mid-1;
}else{
left=mid+1;
}
}
if(left<=n && a[left]==key){
return left;
}
return -1;
}
int FindLastEqual(int key){
int left=1;
int right=n;
while(left<=right){
int mid=(left+right)/2;
if(a[mid]<=key){
left=mid+1;
}else{
right=mid-1;
}
}
if(right>=1 && a[right]==key){
return right;
}
return -1;
}
int FindLastNotMore(int key){
int left=1;
int right=n;
while(left<=right){
int mid=(left+right)/2;
if(a[mid]>key){
right=mid-1;
}else{
left=mid+1;
}
}
return right;
}
int FindLastLess(int key){
int left=1;
int right=n;
while(left<=right){
int mid=(left+right)/2;
if(a[mid]>=key){
right=mid-1;
}else{
left=mid+1;
}
}
return right;
}
int FindFirstNotLess(int key){
int left=1;
int right=n;
while(left<=right){
int mid=(left+right)/2;
if(a[mid]>=key){
right=mid-1;
}else{
left=mid+1;
}
}
return left;
}
int FindFirstMore(int key){
int left=1;
int right=n;
while(left<=right){
int mid=(left+right)/2;
if(a[mid]>key){
right=mid-1;
}else{
left=mid+1;
}
}
return left;
}
void init(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
}
int main(){
freopen("a.txt","r",stdin);
// a.txt
// 15
// 1 1 2 5 5 5 5 5 5 6 7 8 9 11 13
cout<<"15\n 1 1 2 5 5 5 5 5 5 6 7 8 9 11 13\n";
cout<<" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\n";
init();
int x=3;
cout<<"FindFirstEqual(x)"<<FindFirstEqual(x)<<endl;
cout<<"FindLastEqual(x)"<<FindLastEqual(x)<<endl;
cout<<"FindFirstMore(x)"<<FindFirstMore(x)<<endl;
cout<<"FindFirstNotLess(x)"<<FindFirstNotLess(x)<<endl;
cout<<"FindLastNotMore(x)"<<FindLastNotMore(x)<<endl;
cout<<"FindLastLess(x)"<<FindLastLess(x)<<endl;
}
8 上面六種方法匯總
// 這里必須是 <=
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] ? key) {
//... right = mid - 1;
}
else {
// ... left = mid + 1;
}
}
return xxx;
int xxx(int key){
int left=1;
int right=n;
while(left<=right){
int mid=(left+right)/2;
if(a[mid]>=key){
right=mid-1;
}else{
left=mid+1;
}
}
...
return xxx
}
三分問題
可以認為三分求導極值--二分零點
離散型
/\型
int SanFen(int l,int r) //找凸點
{
while(l < r-1)
{
int mid = (l+r)/2;
int mmid = (mid+r)/2;
if( f(mid) > f(mmid) )
r = mmid;
else
l = mid;
}
return f(l) > f(r) ? l : r;
}
\/型
int SanFen(int l,int r) //找凸點
{
while(l < r-1)
{
int mid = (l+r)/2;
int mmid = (mid+r)/2;
if( f(mid) > f(mmid) )
l = mid;
else
r = mmid;
}
return f(l) > f(r) ? l : r;
}
連續型
/\型
double three_devide(double low,double up)
{
double m1,m2;
while(up-low>=eps)
{
m1=low+(up-low)/3;
m2=up-(up-low)/3;
if(f(m1)<=f(m2))
low=m1;
else
up=m2;
}
return (m1+m2)/2;
}
\/型
double three_devide(double low,double up)
{
double m1,m2;
while(up-low>=eps)
{
m1=low+(up-low)/3;
m2=up-(up-low)/3;
if(f(m1)<=f(m2))
up=m2;
else
low=m1;
}
return (m1+m2)/2;
}
應用

#include <iostream>
using namespace std;
double f(double x){
return (0.1*x*x*x-x*x+2*x+1);
}
double sanfenshangtu(double left,double right){
double m1,m2;
double eps=1e-8;
while(right-left>=eps){
m1=left+(right-left)/3;
m2=right-(right-left)/3;
if(f(m1)>f(m2)){
right=m2;
}else{
left=m1;
}
}
return (left+right)/2;
}
double sanfenxiatu(double left,double right){
double m1,m2;
double eps=1e-8;
while(right-left>=eps){
m1=left+(right-left)/3;
m2=right-(right-left)/3;
if(f(m1)<f(m2)){
right=m2;
}else{
left=m1;
}
}
return (left+right)/2;
}
int main(){
cout<<sanfenshangtu(0,10)<<endl<<f(sanfenshangtu(0,10))<<endl;
cout<<sanfenxiatu(0,10)<<endl<<f(sanfenxiatu(0,10))<<endl;
}

[Running] cd "o:\2\" && g++ sanfen.cpp -o sanfen && "o:\2\"sanfen
1.22515
2.1332
5.44152
-1.61468
[Done] exited with code=0 in 0.487 seconds
