SGU 114. Telecasting station 三分or找中位數

題目鏈接點這兒

一開始想都沒想。。。直接上了三分。。。結果。。。sample的答案不一樣。。。但是過了。。。然後又看了看。。。發現這不就是高中或者初中出過的求中位數的題麼。。。直接找到這些的中位數就行了。。。。如果某一點處人口不止一個,那就把它拆成一個一個的點。然後求中位數。嗯。。。這題就結束了。。。


至於三分的過程,其實就是二分稍微改進的版本,二分只能求單調的函數,如果函數在定義域內不是單調的,那麼二分就失效了。當然。。。這時可以用數學上的無窮分逼近,通過分成若干小段逐個二分求區間最值,最後找最大值。

不過有一種現成的算法來解決定義域內爲凹函數或者凸函數(即單峯函數,定義域內只有一個極值點)。二分中用到了3個量,begin, medium, end。而三分則是由加了一個r_medium = (medium + end)/2。

這樣,每次分割後,不是比較的medium和begin或者end的大小,因爲最值不會在某個區間的端點處出現。而是比較medium和r_medium的大小。如果medium比較靠近極值,那麼便捨棄r_medium到end這一段,反之則捨棄begin到medium這一段。這樣,每次捨去的都是離極值較遠的一段,從而保證了每次都沒有將極值丟掉。


比如這個就會捨棄掉begin到medium這一段,很明顯,極值所在區間並沒有捨去

這樣反覆比較,區間就會越來與靠近極值,這時的medium就可近似認爲是所求的極值了


偷懶就把三分的代碼放上來了。。。求中位數的過程也沒有什麼難點。。。

#include <bits/stdc++.h>
#define max(a,b) ((a)>(b))?(a):(b)
#define min(a,b) ((a)>(b))?(b):(a)
#define rep(i,initial_n,end_n) for(int (i)=(initial_n);(i)<(end_n);i++)
#define repp(i,initial_n,end_n) for(int (i)=(initial_n);(i)<=(end_n);(i)++)
#define eps 1.0E-8
#define MAX_N 1010
#define INF 1 << 30
using namespace std;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
typedef long long ll;
typedef unsigned long long ull;

pii a[15010];

 int main() {
    int n;
    scanf("%d", &n);
    int minn = INT_MAX, maxx = INT_MIN;
    rep(i, 0, n) {
        scanf("%d%d", &a[i].first, &a[i].second);
        if(minn > a[i].first) minn = a[i].first;
        if(maxx < a[i].first) maxx = a[i].first;
    }
    double b = minn, e = maxx, m = (b+e)/2, mm = (m+e)/2;
    while(b - e < -eps) {
        double tmp = 0, tmpp = 0;
        rep(i, 0, n) {
            tmp += fabs(a[i].first - m) * 1.0 * a[i].second, tmpp += fabs(a[i].first - mm) * 1.0 * a[i].second;
        }
        if(tmp - tmpp < -eps) e = mm;
        else b = m;
        m = (b+e)/2, mm = (m+e)/2;
    }
    printf("%f\n", m);
    return 0;
 }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章