Problem G. Interstellar Travel

該題涉及凸包問題,下面是有關凸包的博客
這裏寫鏈接內容

Problem G. Interstellar Travel

Problem Description

After trying hard for many years, Little Q has finally received an astronaut license. To celebrate the fact, he intends to buy himself a spaceship and make an interstellar travel.
Little Q knows the position of n planets in space, labeled by 1 to n. To his surprise, these planets are all coplanar. So to simplify, Little Q put these n planets on a plane coordinate system, and calculated the coordinate of each planet (xi,yi).
Little Q plans to start his journey at the 1-th planet, and end at the n-th planet. When he is at the i-th planet, he can next fly to the j-th planet only if xi

Input

The first line of the input contains an integer T(1≤T≤10), denoting the number of test cases.
In each test case, there is an integer n(2≤n≤200000) in the first line, denoting the number of planets.
For the next n lines, each line contains 2 integers xi,yi(0≤xi,yi≤109), denoting the coordinate of the i-th planet. Note that different planets may have the same coordinate because they are too close to each other. It is guaranteed that y1=yn=0,0=x1<x2,x3,...,xn−1<xn.

Output

For each test case, print a single line containing several distinct integers p1,p2,…,pm(1≤pi≤n), denoting the route you chosen is p1→p2→…→pm−1→pm. Obviously p1 should be 1 and pm should be n. You should choose the route with minimum total cost. If there are multiple best routes, please choose the one with the smallest lexicographically.
A sequence of integers a is lexicographically smaller than a sequence of b if there exists such index j that ai=bi for all i

Sample Input

1
3
0 0
3 0
4 0

Sample Output

1 2 3

題意概述

問題g .星際旅行

問題描述

經過多年的努力,小Q終於獲得了宇航員執照。爲了慶祝這一事實,他打算給自己買一艘宇宙飛船,進行一次星際旅行。

小Q知道n個行星在空間中的位置,被1到n標記,令他吃驚的是,這些行星都是共面的。爲了簡化,小Q把這n顆行星放在平面座標系中,計算出每顆行星的座標(xi,yi)

小Q計劃在第1顆行星開始他的旅程,在第n顆行星結束。當他在第i個行星時,他可以從第i行星上飛到第j個行星,這將花費他的飛船xi*yj-xj*yi單位的能量。注意,這個成本可以是負的,這意味着飛行將爲他的宇宙飛船提供補給。

請編寫一個程序幫助小Q以最小的總成本找到最佳路線。

輸入

輸入的第一行包含一個整數T(1≤T≤10),表示測試用例的數量。

在每個測試用例,一個整數n(2≤n≤200000)在第一線,表示數量的行星。

在接下來的n行,每一行包含兩個整數,xi,yi(0≤xi,yi≤10^9)表示第i個星球的座標。注意,不同的行星可能有相同的座標,因爲它們彼此太接近了。測試數據保證“y1=yn=0,0=x1<x2,x3,...,xn−1<xn. ”

輸出

對於每個測試用例,打印一行包含幾個不同的整數p1,p2,…,pm(1≤pi≤n),表示你選擇的路線是p1→p2→…→pm−1→pm。顯然p1應該是1,pm應該是n,你應該選擇總成本最小的路線。如果有多個最佳路徑,請選擇字典上最小的路徑。

一個整數序列A在字典上比b的序列要小,如果存在這樣的索引j 使ai=bi對於所有i<j,aj<bj

思路

由於從i星球到j星球需要消耗的能量爲 xi×yj−xj×yicost=xi×yj−xj×yi。可以發現這是一個求向量叉積的題,因爲要使總成本最小。且這個成本可以是負的,那麼負的越大,成本越小,這樣我們就可以想到用凸包。
又要求按字典序最小輸出。 那麼我們就要去掉一些不必要的點:
求出來的是一個凸包,如果凸包上沒有三點或三點以上共線的情況,那麼就直接把凸包上的點全部輸出就好了。
但是如果有多點共線的情況,線段的2個端點肯定是必選的(不然就不是線段了)。只是對於處於線段中的點,如果選了那個點能使字典序變小,則選上,否則不選。

代碼
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+7;
struct node{
    int x,y,ps;
    bool operator<(const node &p)const{   //排序時用
        if(x != p.x) return x < p.x;
        if(y != p.y) return y > p.y;
        return ps < p.ps;
    }
}pt[N];

long long get(int x,int y,int z){   //用來求解xi×yj−xj×yi
    int ax = pt[y].x-pt[x].x;
    int ay = pt[y].y-pt[x].y;
    int bx = pt[z].x-pt[y].x;
    int by = pt[z].y-pt[y].y;
    return 1LL*ax*by-1LL*ay*bx;
}

int mn[N];

int que[N];
int main(){
    int T;
    cin >> T;
    while(T--){
        int n;
        scanf("%d",&n);
        for(int i = 1;i <= n;i ++) scanf("%d %d",&pt[i].x,&pt[i].y); //輸入
        for(int i = 1;i <= n;i ++) pt[i].ps = i;    //編號
        sort(pt+1,pt+n+1);    //排序求解上凸包
        int now = 0;
        que[0] = 1;
        for(int i = 2;i <= n;i ++){
            if(pt[i].x == pt[i-1].x) continue;
            while(now && get(que[now-1],que[now],i)>0) now --;
            que[++now] = i;
        }                  //求解上凸包結束
        int l = 0,r;
        printf("1");
        while(l < now){
            for(r = l+1;r <= now;r ++){
                if(get(que[l],que[l+1],que[r])!= 0) break;  //看是否存在共線情況
            }
            mn[r] = 1e9;
            for(int i = r-1;i >= l;i --)
            {
                mn[i] = min(pt[que[i]].ps,mn[i+1]);  //對於沒選中的點,和後面已經選的點進行比較,取字典序較小的
            }
            for(int i = l+1;i <= r-1;i ++) 
                if(mn[i] == pt[que[i]].ps) printf(" %d",pt[que[i]].ps);     //如果i這一點的下標值 與判別是否取該點時生成的下標值mn[i]相同,取該點,否則刪除該點
            l = r-1;
        }
        puts("");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章