zoj3862 intersection[2015浙大校赛C题]


题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5474


题意:

按1-2n的编号给定2n个点以及n条线段,每个点属于且仅属于一条线段。

线段用端点的编号确定。

现在一次操作可以交换两个点的编号

要求在不超过n+10次的操作后使得任意两条线段不相交。

输入保证任意两个点不重合,任意三个点不相交。


思路:

首先要明确,不是要求最少操作次数,只要不超过n+10次就可以。

所以一定有解:

step 1: 点配对

    把所有点按水平序排好,人为配对:排名2k-1的点和排名2k的点组成一条线段。(2k-1并不是点的编号,只是排序后的位置)

    这样的话没有线段会相交。


step 2: 统计操作次数

    点已经配好了,只要计算从原来的配对变成现在的配对需要多少次操作。

    假设排序后的点编号是:a, b, ... , c, ... , d , ....

    本来是a和c组成一条线段,b和d组成一条线段

    为了让a和c配对,交换b和c的编号(其实是交换b和c对应的点坐标。。。)

    显然最多进行n次操作


总之step 1比较简单,step 2我讲不清楚。。。

把握一点:操作之后,点的编号对应关系不变。

比如说1号点一开始与4号点组成线段,操作之后还是1号点和4号点组成线段

但是1号点和4号点的坐标可能和之前不一样

理解这一点就好了。。。


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

const int N = 200000;
int a[N+10], rk[N+10], f[N+10];
int ansx[N+10], ansy[N+10];
int n;
struct point{
    int x, y, id;
    void input(){
        scanf("%d %d", &x, &y);
    }
}p[N+10];
bool cmp(point a, point b){
    if (a.x == b.x) return a.y < b.y;
    return a.x < b.x;
}
int main(){
    int test;
    scanf("%d", &test);
    while (test--){
        scanf("%d", &n);
        for (int i=1; i<=2*n; i++){
            p[i].input();
            p[i].id = i;
        }
        int a, b;
        for (int i=1; i<=n; i++){
            scanf("%d %d", &a, &b);
            f[a] = b; f[b] = a;
        }
        int nn = 2*n;
        sort(p+1, p+n*2+1, cmp);
        for (int i=1; i<=nn; i++)
            rk[p[i].id] = i;
        int cnt = 0;
        for (int i=1; i<=nn; i+=2){
            int a = p[i].id, b = p[i+1].id;
            if (f[a] != b){
                int c = f[a], d = f[b];
                cnt++;
                ansx[cnt] = b; ansy[cnt] = c;
                swap(p[i+1].id, p[rk[c]].id);
                swap(rk[b], rk[c]);
            }
        }
        printf("%d\n", cnt);
        for (int i=1; i<=cnt; i++)
            printf("%d %d\n", ansx[i], ansy[i]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值