算法设计——分治法

时间:2022-07-20 11:06:24

我是搞不明白为什么这东西叫算法,还是感觉叫策略好一些。。。

下面是用分治法计算最近点对问题,在程序跑出来之前先把代码记录一下

基本想法:输入N个点(x,y),按x从小到大排序(可以调用C++自带的sort(vec.begin(),vec.end())进行排序)

将这个点集在x轴的中间那个点一分为二,分别查找两边的最小距离和对应的两个点,查找方法还是一分为二,一分为二……直到不能再分(一个点集只有一个或两个点)

找到最小距离和那两个点后,假设当前这个点集是上一个点集分出来的左半部分,那么将它与右半部分合并,找两个点集合起来后的最小距离:这时不用想暴力求解那样两两计算距离,有一种更省力的方法——

对于这个点集左半部分的所有点,在右边最多有和它在y轴方向上最接近的6个点可能有比两边的最小距离更小的距离,所以计算6*n个距离即可(n表示左半边的点数),计算出来的最小的距离,一定就是左右合并起来的点集里的最短距离。

 

上代码:

 

#include <iostream>

///分治算法、蛮力法最近点对问题
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>

#define INF 0xFFFFFF
using namespace std;

class point {
private:
    double x;
    double y;
public:
    explicit point(double _x = 0, double _y = 0) {
        x = _x;
        y = _y;
    }

    double getx() {
        return x;
    }

    double gety() {
        return y;
    }

    double operator-(point p) {
        return sqrt(pow(x - p.getx(), 2) + pow(y - p.gety(), 2));
    }

    bool operator<(point p) {
        return x < p.getx();
    }

    bool operator^(point p) {
        return y > p.gety();
    }
};

vector<point> pointsX;
vector<point> pointsY;

void merge(int left, int right) {
    int mid = (left + right) / 2;
    int l = left, r = mid + 1;
    int index = l;

    while (l <= mid && r <= right) {
        if (pointsX[r] ^ pointsX[l]) {
            pointsY[index++] = pointsX[l++];
        } else {
            pointsY[index++] = pointsX[r++];
        }
    }
    while (l <= mid) {
        pointsY[index++] = pointsX[l++];
    }
    while (r <= right) {
        pointsY[index++] = pointsX[r++];
    }
    for (l = left; l <= right; l++) {
        pointsX[l] = pointsY[l];
    }
}

double divide(vector<point> px, int l, int r, point &p1, point &p2) {
    if (r - l <= 0) {
        return INF;
    } else if (r - l == 1) {
        merge(l, r);
        return px[l] - px[r];
    } else {
        point lp1, lp2;
        point rp1, rp2;
        double min_distance;
        int i, j;
        int mid = (r + l) / 2;

        double middle_x = pointsX[mid].getx();

        double min_distance_l = divide(px, l, mid, lp1, lp2);
        double min_distance_r = divide(px, mid + 1, r, rp1, rp2);
        if (min_distance_l < min_distance_r) {
            min_distance = min_distance_l;
            p1 = lp1;
            p2 = lp2;
        } else {
            min_distance = min_distance_r;
            p1 = rp1;
            p2 = rp2;
        }

        vector<point> temp(r - l + 1);
        int number = 0;

        merge(l, r);

        for (i = l; i <= r; i++) {
            if (fabs(px[i].getx() - middle_x) <= min_distance) {
                temp[number++] = px[i];
            }
        }

        double temp_distance;

        for (i = 0; i < number; i++) {
            for (j = i + 1; j < i + 1 + 6 && j < number; j++) {
                temp_distance = temp[i] - temp[j];
                if (temp_distance < min_distance) {
                    min_distance = temp_distance;
                    p1 = temp[i];
                    p2 = temp[j];
                }
            }
        }
        return min_distance;
    }
}

double violent(vector<point> &vec, point &p1, point &p2) {
    double minDistance = INF;
    int size = vec.size();
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            if (i != j) {
                double d = vec[i] - vec[j];
                if (d < minDistance) {
                    minDistance = d;
                    p1 = vec[i];
                    p2 = vec[j];
                }
            }
        }
    }
    return minDistance;
}

int main() {
    int n = 100000;
    vector<double> x = vector<double>(n);
    vector<double> y = vector<double>(n);
    srand(10);
    for (int i = 0; i < n; i++) {
        x[i] = rand() / 10000.0;
        y[i] = rand() / 10000.0;
    }
    clock_t start;
    clock_t end;

    start = clock();
    sort(x.begin(), x.end());
    int size = x.size();
    vector<point> points = vector<point>(size);
    pointsX = vector<point>(size);
    pointsY = vector<point>(size);
    for (int i = 0; i < size; i++) {
        points[i] = point(x[i], y[i]);
        pointsX[i] = point(x[i], y[i]);
        pointsY[i] = point(x[i], y[i]);
    }
    point p1, p2;
    divide(points, 0, size - 1, p1, p2);
    end = clock();
    cout << "\n Time consumed= " << (double) end - start << endl;
    cout << "\nMin distance: " << p1 - p2 << endl;
    cout << "\np1: (" << p1.getx() << "," << p1.gety() << ")" << endl;
    cout << "\np2: (" << p2.getx() << "," << p2.gety() << ")" << endl;

    cout << "\n---Violent method---" << endl;
    start = clock();
    violent(points, p1, p2);
    end = clock();
    cout << "\n Time consumed= " << (double) end - start << endl;
    cout << "\nMin distance: " << p1 - p2 << endl;
    cout << "\np1: (" << p1.getx() << "," << p1.gety() << ")" << endl;
    cout << "\np2: (" << p2.getx() << "," << p2.gety() << ")" << endl;
}

 另一个问题——比赛时间表问题

问题很简单,直接贴代码

///分治算法循环赛日程表安排问题
#include <iostream>
#include <vector>
#include <cmath>
#include <cstdlib>

using namespace std;

void gameTable1(vector<vector<int>> &vec) {
    int s = vec.size();
    int k = 0;
    k = round(log(s) / log(2));
    //初始化
    vec[0][0] = vec[1][1] = 1;
    vec[0][1] = vec[1][0] = 2;

    for (int i = 2; i <= k; i++) {
        int length = (int)pow(2,i);
        int half = length/2;
        for(int row=0;row<half;row++) {
            for(int col=0;col<half;col++) {
                //左下角的子表中项为左上角子表对应项加half=2^(i-1)
                vec[row + half][col] = vec[row][col] + half;
                //右上角的子表等于左下角子表
                vec[row][col + half] = vec[row + half][col];
                //右下角的子表等于左上角子表
                vec[row + half][col + half] = vec[row][col];
            }
        }
    }
}

bool isOdd(int n){
    return n/2.0-n/2>0.1;
}

void gameTable2(vector<vector<int>> &table){
    int num_of_team=table.size();
    for(int i=0;i<num_of_team;i+=2){
        for(int j=0;j<num_of_team;j+=2){
            table[i][j]=table[i+1][j+1]=(i+j)%num_of_team+1;
            table[i][j+1]=table[i+1][j]=(i+j+1)%num_of_team+1;
        }
    }
}

int main(){
    int size=10;
    vector<vector<int>> table=vector<vector<int>>(size,vector<int>(size));
    gameTable2(table);
    for(int i=0;i<size;i++){
        for(int j=0;j<size;j++){
            cout<<table[i][j]<<"\t";
            if(j==0) cout<<"- ";
        }
        cout<<endl;
    }
    return 0;
}