ZOJ 3862 思维

题目链接


题意:给定 n 条线段和 2n 个点,不存在点重合和三点共线的情况,假设对于两条相交的线段,我们可以通过交换其中的两个点的位置,使线段不相交,记交换动作为一次操作。问对于给定的 n 条线段,问是否可以在 n+10 次操作内使所有线段两两不相交,如果可以的话,输出操作次数和具体每一次操作交换的两个点序号。


思路:
对于 n<=1e5 这个数据范围,自然排除了 O(n2) 判两两线段是否相交等等的 2D 计算几何算法。

通过多次画图可以发现,我们可以按照横坐标或纵坐标排序甚至极角进行排序。

随后相邻最近的两个点相连成一个线段,这样一定不存在两个相交的线段。
而且每一个合法的线段最多只需要一次操作便可得到,即总操作数不超过 n 次。
故不存在无法完成输出 1 的情况。

此题得解。


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

const int A = 1e5 + 10;
class P{
public:
    int x,y,id;
    bool operator<(const P& rhs)const{
        if(x != rhs.x) return x < rhs.x;
        return y < rhs.y;
    }
}a[A<<2];
int con[A<<2],pos[A<<2];
vector<pair<int,int> > vec;

int main(){
    int T;scanf("%d",&T);
    while(T--){
        int n;scanf("%d",&n);
        for(int i=1 ;i<=2*n ;i++){
            a[i].id = i;
            scanf("%d%d",&a[i].x,&a[i].y);
        }
        for(int i=1 ;i<=n ;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            con[u] = v;con[v] = u;
        }

        sort(a+1,a+1+2*n);
        vec.clear();
        for(int i=1 ;i<=2*n ;i++){
            pos[a[i].id] = i;
        }

        for(int i=1 ;i<=2*n; i+=2){
            int u = a[i].id,v = a[i+1].id;

            if(con[u] == v) continue;
            int tem = con[u];
            vec.push_back(make_pair(tem,v));

            a[pos[tem]].id = v;
            swap(pos[tem],pos[v]);
        }
        int Siz = vec.size();
        printf("%d\n",Siz);
        for(int i=0 ;i<Siz ;i++){
            printf("%d %d\n",vec[i].first,vec[i].second);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wubaizhe/article/details/79840576