序:
二分搜索是很常見的一種算法,用來在有序序列中尋找某數(或者小於大於它的最*值的某數)。
二分答案也比較常見,估算出答案範圍後二分縮小範圍逼近答案。
二分推動與二分答案很像,不一樣之處是二分推動並不是獲得答案的範圍,而是經過縮小答案對應的數據的範圍,逼近答案對應的數據(一般求最值)…ios
舉個例子:
平面上有n個點,已知每一個點的座標。求一個點m(x,y),使得m與其餘全部點的距離和最短。(x,y的精度爲0.1,最終的距離和 < 最小值+0.01便可)算法
思路一:暴力枚舉。
因爲精度是0.1,因此咱們能夠從minx -> maxx, miny -> maxy枚舉與全部點的距離,取最小。複雜度O(100n^2),n大一點即便忽略常數100(1/0.1*1/0.1)也得TLE。bash
思路二:二分推動。
盡然咱們想到了枚舉,並且咱們知道最小值的座標是惟一的。(不知道怎麼證,憑直覺這應該是顯然的…)
既然如此,假設這個點是m(x,y),對於m周邊的全部點必定距離和都要大於它的距離和(不然就不是最小了)。
還有一個隱含條件:minx<=x<=maxx; miny<=y<=maxy;
最重要的一點,越接近m的點的距離和應該越接近最小值(答案)。
知道範圍,又知道答案的特殊性與數據範圍的單調性,那麼就可使用二分推動算法逼近了。markdown
思路是這樣的:
1.選取範圍內的任意一個點。
2.計算它的距離和以及它上下左右±r,也就是(x+r,y),(x,y+r)…四個座標的距離和,若是出現ans < minn,則至關於出現更優解,意味着答案應該更靠近那個點,因此咱們將當前點更新爲那個點繼續操做。
3.可能會出現上下左右4個點都比如今座標大,那看來是幅度太大了,r = r/2重複上一步操做。優化
僞代碼是這樣的:ui
while(步長大於偏差)
{
flag = true;
while(flag)
{
先不容許下次以當前步長搜索
for(四個方向)
{
計算當前座標的距離和;
if(當前的答案更小)
{
容許下次繼續以當前步長搜索
更新當前座標與最小答案
}
}
}
縮小步長
}
最終的座標就是答案。spa
代碼實現:code
/* About: T1 二分推動 Auther: kongse_qi Date:2017/04/29 */
#include <cstdio>
#include <iostream>
#include <cmath>
#define maxn 10005
#define INF 0x7fffff
#define eps 0.01
using namespace std;
int n, a[maxn][2];
double maxx, maxy, minx, miny;
double ans[2], cur_ans = INF;
void Read()
{
double x, y;
scanf("%d", &n);
for(int i = 0; i != n; ++i)
{
scanf("%d%d", &a[i][0], &a[i][1]);
x = double(a[i][0]);
y = double(a[i][1]);
maxx = max(maxx, x);
minx = min(minx, x);
maxy = max(maxy, y);
miny = min(miny, y);
}
return ;
}
double Cal(double x, double y)
{
double ans = 0, x1, y1;
if(x > maxx || y > maxy || x < minx || y < miny) return cur_ans+1;
for(int i = 0; i != n; ++i)
{
x1 = a[i][0];
y1 = a[i][1];
ans += sqrt((x-x1)*(x-x1)+(y-y1)*(y-y1));
}
return ans;
}
void Solve()
{
int ti;
double r = max(maxx-minx, maxy-miny);
bool flag;
double curx = (minx+maxx)/2, cury = (miny+maxy)/2;
int dicx[] = {0, 1, 0, -1}, dicy[] = {1, 0, -1, 0};
double ne_ans;
cur_ans = Cal(curx, cury);
while(r >= eps)
{
flag = true;
while(flag)
{
flag = false;
for(int i = 0; i != 4; ++i)
{
ne_ans = Cal(curx+r*dicx[i], cury+r*dicy[i]);
if(ne_ans < cur_ans)
{
flag = true;
curx += r*dicx[i];
cury += r*dicy[i];
cur_ans = ne_ans;
}
}
}
r *= 0.5;
}
ans[0] = curx;
ans[1] = cury;
return ;
}
int main()
{
freopen("test.in", "r", stdin);
Read();
Solve();
printf("%.1f %.1f", ans[0], ans[1]);
return 0;
}
這與啓發式搜索很像,可是尚未搞懂怎個啓發式。做爲與二分答案法的姊妹算法,暴力代碼後也能夠看看可否經過二分優化。string
至此結束。
箜瑟_qi 2017.04.29 23:49it