Go Running(坐标旋转,二分图最小点覆盖)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6808

题意:若干人在 x 轴上跑步,每个人的开始时间、停止时间、跑的方向、起跑位置都是未知的,但是速度一定为 1m / s,然后有一些机器,记录了 n 条记录,每条记录包含两个信息:xi,ti;表示在 ti 时刻在 xi 未知有一个人经过,问最少有多少人参加跑步。

Input

T(1 <= T <= 100)组样例,每组样例一个 n (1 <= n <= 1e5) 表示共有 n 条记录,然后 n 行,每行两个值 ti,xi (≤ tix≤ 1e9)。

Output

输出最少有多少人参加跑步

Sample Input

2

3

1 1

2 2

3 1

3

1 1

2 2

3 3

Sample Output

2
1

思路:首先画出 x - t 图像,每个人跑出来的直线一定是斜率为 -1 或 1 的直线,那么问题转化为最少用几条斜率为 -1 或 1 的直线覆盖所有点,这与二分图的最小点覆盖问题很相似,但我们无法建图,所以我们 45° 旋转坐标系,此时一个点最后的运动直线或平行于 x 轴或平行与 y 轴,假设点(x, y)旋转后的坐标为(x1, y1),那么我们把 x1 当做二分图左边的点,y1当做二分图右边的点,两点建边,因为二分图的最小点覆盖 = 二分图最大匹配,所以我们对这个图跑一遍二分图匹配问题就解决了。

二分图总结【最大匹配、最小点覆盖、最少路径覆盖和最大独立集】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long ll;

const int N = 200010;

int n, uN, tot;
vector<int> G[N];
int Mx[N], My[N];
int dx[N], dy[N];
int dis;
bool used[N];
unordered_map<double, int> mx, my;

bool SearchP() {
    queue<int> Q;
    dis = inf;
    memset(dx, -1, sizeof(dx));
    memset(dy, -1, sizeof(dy));
    for(int i = 0; i < uN; i++) {
        if(Mx[i] == -1) {
            Q.push(i);
            dx[i] = 0;
        }
    }
    while(!Q.empty()) {
        int u = Q.front();
        Q.pop();
        if(dx[u] > dis) {
            break;
        }
        int sz = (int)G[u].size();
        for(int i = 0; i < sz; i++) {
            int v = G[u][i];
            if(dy[v] == -1) {
                dy[v] = dx[u] + 1;
                if(My[v] == -1) {
                    dis = dy[v];
                } else {
                    dx[My[v]] = dy[v] + 1;
                    Q.push(My[v]);
                }
            }
        }
    }
    return dis != inf;
}

bool dfs(int u) {
    int sz = (int)G[u].size();
    for(int i = 0; i < sz; i++) {
        int v = G[u][i];
        if(!used[v] && dy[v] == dx[u] + 1) {
            used[v] = true;
            if(My[v] != -1 && dy[v] == dis) {
                continue;
            }
            if(My[v] == -1 || dfs(My[v])) {
                My[v] = u;
                Mx[u] = v;
                return true;
            }
        }
    }
    return false;
}

int MaxMatch() {
    int res = 0;
    memset(Mx, -1, sizeof(Mx));
    memset(My, -1, sizeof(My));
    while(SearchP()) {
        memset(used, false, sizeof(used));
        for(int i = 0; i < uN; i++) {
            if(Mx[i] == -1 && dfs(i)) {
                res++;
            }
        }
    }
    return res;
}

int main() {
    int t;
    int u, v, x, y;
    double z = sqrt(2) / 2;
    scanf("%d", &t);
    while(t--) {
        mx.clear();
        my.clear();
        uN = 0;
        scanf("%d", &n);
        tot = n;
        for(int i = 0; i <= n; i++) G[i].clear();
        for(int i = 1; i <= n; i++) {
            scanf("%d %d", &x, &y);
            double xx = z * (x + y);
            double yy = z * (y - x);
            if(!mx.count(xx)) mx[xx] = uN++;
            if(!my.count(yy)) my[yy] = ++tot;
            u = mx[xx];
            v = my[yy];
            G[u].push_back(v);
        }
        printf("%d\n", MaxMatch());
    }
    return 0;
}
View Code

 

posted @ 2020-07-30 22:34  LightandDust  阅读(364)  评论(0)    收藏  举报