1. 程式人生 > 實用技巧 >[School Regional Team Contest, Saratov, 2011] - J. Minimum Sum (分治,幾何)

[School Regional Team Contest, Saratov, 2011] - J. Minimum Sum (分治,幾何)

[School Regional Team Contest, Saratov, 2011] - J. Minimum Sum (分治,幾何)

題面:

題意:

在二維平面上給定\(\mathit n\)個向量,對於每一個向量,你可以使用對應的操作使其座標變為相反數,現在讓你選擇兩個向量\(v_i,v_j\)和其對應的操作\(k_1,k_2\),使其$|v_i^{k_1} +v_j^{k_2} | $ 最小。如果有多種方案,請輸出任意一個。

思路:

通過觀察四種操作,即可以對向量進行對座標軸對稱,和對原點對稱。

那麼我們將其問題先轉化為$|v_i^{k_1} -v_j^{k_2} | $ (只要變化一下\(k_2\or k_1\)

就可以轉化為$|v_i^{k_1} +v_j^{k_2} | $),

通過幾何知識分析可得:$|v_i^{k_1} -v_j^{k_2} | $ ,就是向量\(v_i^{k_1} ,v_j^{k_2} \)的座標表示法時對應的二維平面點的距離。

那麼我們將所有點(將向量的起點平移到原點對應的端點。)轉到第一象限,

然後用分治法求二維平面點的最短距離,同時維護出最短距離點對對應的向量資訊,在輸出時將一個\(\mathit k\) 轉一下即可。

程式碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;

int MAX = 1e9; //定義的最大距離,以在只有一個點的時返回無窮大
int a, b;  //用來記錄下標,與題無關
struct Node {
    int x, y;
    int key;   //關鍵碼,可有可無,與ab有關
    int type;
};

Node ar[100005], br[100005];

bool cmpx(Node a, Node b) {return a.x < b.x;} //x座標升序
bool cmpy(Node a, Node b) {return a.y < b.y;} //y座標升序
int min(int a, int b) {return a < b ? a : b;} //返回最小值
int dis(Node a, Node b) {return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);} //返回點與點之間的距離
int ans1, ans2, ans3, ans4;
int ans_dis = 1e9;
int cal(int s, int e) //s、e用來表示當前處理的陣列中的下標位置
{
    int mid, i, j, tail = 0; //mid表示陣列中間的位置下標 ,tail作為計數變數,是用來br陣列儲存標號的
    int d;   //d表示點對之間的距離
    if (s == e) { return MAX; } //如果只有一個點
    mid = (s + e) / 2;
    d = min(cal(s, mid), cal(mid + 1, e)); //遞迴求出左右兩邊的最小距離      //下面是求是否存在左邊的點到右邊某點的距離小於d的點,或者是否存在在右邊的點到左邊某點的距離小於d的點,若是存在,必定處於一個d*2d的矩形中
    for (i = mid; i >= s && (ar[mid].x - ar[i].x) * (ar[mid].x - ar[i].x) < d; i--) { //篩選左邊的點,在中間位置左側d以內的點
        br[tail++] = ar[i];
    }

    for (i = mid + 1; i < e && (ar[i].x - ar[mid].x) * (ar[i].x - ar[mid].x) < d; i++) { //同上,篩選右邊的點
        br[tail++] = ar[i];
    }
    sort(br, br + tail, cmpy);// sort for y
    for (i = 0; i < tail; i++) {//列舉矩形內點對之間的距離
        for (j = i + 1; j < tail && (br[j].y - br[i].y) * (br[j].y - br[i].y) < d; j++) {
            if (d > dis(br[i], br[j])) {      //更新點的值
                d = min(d, dis(br[i], br[j]));
                if (d < ans_dis) {
                    ans_dis = d;
                    ans1 = br[i].key;
                    ans2 = br[i].type;
                    ans3 = br[j].key;
                    ans4 = br[j].type;
                }
            }
        }
    }
    return d;                     //返回最小的點對之間的距離
}

int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        ar[i].key = i + 1;           //關鍵碼賦值
        scanf("%d %d", &ar[i].x, &ar[i].y);
        if (ar[i].x >= 0 && ar[i].y >= 0) {
            ar[i].type = 1;
        } else  if (ar[i].x < 0 && ar[i].y >= 0) {
            ar[i].type = 2;
        } else  if (ar[i].x >= 0 && ar[i].y < 0) {
            ar[i].type = 3;
        } else  if (ar[i].x < 0 && ar[i].y < 0) {
            ar[i].type = 4;
        }
        ar[i].x = abs(ar[i].x);
        ar[i].y = abs(ar[i].y);
    }
    sort(ar, ar + n, cmpx);        //按x對ar排序
    int d = cal(0, n);
    if (ans4 == 2) {
        ans4 = 3;
    } else if (ans4 == 3) {
        ans4 = 2;
    } else if (ans4 == 1) {
        ans4 = 4;
    } else {
        ans4 = 1;
    }
    printf("%d %d %d %d\n", ans1, ans2, ans3, ans4 );
    return 0;
}