Pub crawl

Al just arrived in Dublin. He is going to spend his cash on the famous Dublin activity - the pub crawl. The goal is to drink a pint of Guinness in as many different pubs in the city as possible, not visiting any pub twice. There are n pubs in Dublin. Al gets drunk very fast, so he sees pubs as points on a plane, and his path from a pub to the next one as a straight line connecting these points, though the actual path may involve going along different streets, around buildings or even walking in circles trying to find the next pub. Al doesn’t care about those details. The only thing he cares about is that every turn he makes is a left turn, he enjoys going left. This means for every three consecutive pubs in his route the third one must lie in the left half plane with respect to the directed line from the first pub to the second. It is known that builders in Dublin also enjoy pub crawl, so they never managed to build at least three pubs on a straight line. Help Al to find out how many pubs he can visit and plan the route for him.  
Input  
The first line contains an integer n (1 ≤ n ≤ 5000) – the number of pubs. Each of the next n lines contains two integers x and y (−109 ≤ x, y ≤ 109) – pub coordinates. Different pubs are located in different points.  
Output  
The first line should contain an integer m – the maximum number of pubs Al can visit. The next line should contain m indices of pubs in the order Al should visit them. Pubs are numbered from 1 to n in the same order as they appear in the input.   

 Sample input

0 4 3 0 7 11 9 1 13 8 

Sample output 

5 1 4 3 2

题意:给出n个点的坐标,题目保证不会出现三点共线,然后随便选择一个点,每次只往左走,即,对于路线上的每三个点,第三个点必须在前两个点的连线的左边。

题解:看到题意,想了一下,感觉不难,也很容易感觉的出和凸包很像,虽然我并不会凸包,但是以为很容易。虽然比赛期间有队友打码很快过了,不过后来补题的时候wa了一遍又一遍,才发现代码实现起来不容易,况且以前都没学过凸包,只是单单知道凸包的定义罢了。最后看了一下模板,理解一下,debug几次ac后,发现凸包的模板很简单,也很容易理解,就不多解释了。

其实本题就是一个凸包的等定义代换,凸包,其实也可以理解为每次只能往左边走,最后围起来的就是凸包。

所以这道题,一开始随意选个起点,找一个凸包,然后保留路线的最终点,去掉凸包上的其他点,(用一个vis数组标记就可以了),然后继续找凸包,直到点全部找完就可以了。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <stack>
#include <cstring>
#include <vector>
#include <bitset>
#include <string>
#include <cmath>
#include <set>
#include <map>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn = 1e6+7;
const int INF = 0x3f3f3f3f;
int n;
int vis[5005];
vector<int> ans;
struct X{
    ll x,y,id;
    X(ll x = 0,ll y = 0,ll id = 0):x(x),y(y),id(id) {}
    X operator - (const X &a){
        return X(x-a.x,y-a.y);
    }
    ll det(const X &a){
        return x*a.y-y*a.x;
    }
};
X s[5005];

bool cmp(X a,X b){
    if(a.x==b.x) return a.y < b.y;
        else return a.x < b.x;
}

vector<X> work(X *p,int n){
    int cap = 0;
    vector<X> tub(n*2);
    for(int i = 1;i <= n;i++){
        if(vis[p[i].id]) continue;
        while(cap>1&&(tub[cap-1]-tub[cap-2]).det(p[i]-tub[cap-1])<=0) cap--;
        tub[cap++] = p[i];
    }
    for(int i = n-1,t = cap;i >= 1;i--){
        if(vis[p[i].id]) continue;
        while(cap>t&&(tub[cap-1]-tub[cap-2]).det(p[i]-tub[cap-1])<=0) cap--;
        tub[cap++] = p[i];
    }
    tub.resize(cap-1);
    return tub;
}


void solve(){
    sort(s+1,s+n+1,cmp);
    memset(vis,0,sizeof(vis));
    vector<X> preans;
    int si = s[1].id;
    int now = n;
    while(now>1){
        preans = work(s,n);
        now-=preans.size()-1;
        for(int i = 0;i < preans.size();i++){
            if(preans[i].id == si){
                int j = i;
                for(;j < i+preans.size()-1;j++){
                    vis[preans[j%preans.size()].id] = 1;
                    printf("%d ",preans[j%preans.size()].id);
                }
                si = preans[j%(preans.size())].id;
                break;
            }
        }
    }
    printf("%d\n",si);
}

int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i = 1;i <= n;i++){
            int x,y;
            scanf("%d %d",&x,&y);
            s[i].x = x;
            s[i].y = y;
            s[i].id = i;
        }
        printf("%d\n",n);
        solve();
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值