1007 Go Running(hdu6808)
题意:有一条无限长的跑道,有一些人正在以1m/s的速度匀速跑步,有一台检测机在 t i t_i ti检测到有同学在 x i x_i xi的位置,询问最少有多少个人在跑步。
解法:首先可以建立一个直角坐标系,横坐标代表时间,纵坐标代表位置,对于1m/s的匀速运动,那么一个人的运动轨迹在这样一个坐标系中斜率肯定是1或-1,那么题意就可以转换成将这些点斜率为1和-1的直线画出来,之后再看经过所有点的最少直线。
如果将这个图顺时针转动45度,可以想到这是二分图匹配的经典问题,最小点覆盖=最大匹配,这里我用了网络流的方法实现,建立超级源点和超级汇点。具体建边方式就是源点连向所有涉及到的行权值为1(注意重复的不叠加),所有涉及到的列连向汇点权值为1(注意重复不叠加),再对于每个点行和列建一条权值为1的边,再跑一边最大流就行。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int M = 2e5+10;
const int INF = 0x3f3f3f3f;
struct xx {
int x, y, a, b, lx, ly;
}p[N];
bool cmp1(const xx &p1, const xx &p2) {
return p1.a < p2.a;
}
bool cmp2(const xx &p1, const xx &p2) {
return p1.b > p2.b;
}
struct node {
int to, dis, next;
}Ed[M*2];
int head[M], cur[M], d[M], cnt = 1;
int tot, lst, s, t;
void Add(int from, int to, int dis) {
Ed[++cnt] = node {to, dis, head[from]};
head[from] = cnt;
Ed[++cnt] = node {from, 0, head[to]};
head[to] = cnt;
}
bool bfs() {
memset(d, -1, sizeof(d));
queue<int>q;q.push(s);
int u, _new;
d[s] = 0;
while(!q.empty()) {
u = q.front(); q.pop();
for(int c_e = head[u]; c_e != -1; c_e = Ed[c_e].next) {//增广路
_new = Ed[c_e].to;
if(d[_new] == -1 && Ed[c_e].dis > 0) {
d[_new] = d[u] + 1;
q.push(_new);
}
}
}
return (d[t] != -1);
}
int dfs(int u, int flow) {
//cout << u << " " << flow << endl;
if(u == t) return flow;
int _flow = 0, __flow;
for(int &c_e = cur[u]; c_e != -1; c_e = Ed[c_e].next) {//当前弧优化
int v = Ed[c_e].to;
if(d[v] == d[u] + 1 && Ed[c_e].dis > 0) {
__flow = dfs(v, min(flow, Ed[c_e].dis));
flow -= __flow;
Ed[c_e].dis -= __flow;
_flow += __flow;
Ed[c_e^1].dis += __flow;
if(!flow) break;
}
}
if(!_flow) d[u] = -1;
return _flow;
}
void dinic() {
int max_flow = 0;
while(bfs()) {
for(int i = 0; i <= t; ++i) cur[i] = head[i];
max_flow += dfs(s, INF);
}
printf("%d\n", max_flow);
}
int vis[M];
int main() {
int T; scanf("%d", &T);
int n;
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &p[i].x, &p[i].y);
p[i].a = p[i].x + p[i].y;
p[i].b = p[i].y - p[i].x;
}
sort(p+1, p+n+1, cmp1);
tot = 1, lst = p[1].a;
p[1].ly = tot;
for(int i = 2; i <= n; ++i) {
if(p[i].a == lst) p[i].ly = tot;
else p[i].ly = ++tot;
lst = p[i].a;
}
sort(p+1, p+n+1, cmp2);
lst = p[1].b;
p[1].lx = ++tot;
for(int i = 2; i <= n; ++i) {
if(p[i].b == lst) p[i].lx = tot;
else p[i].lx = ++tot;
lst = p[i].b;
}
//建边
cnt = 1;
s = 0; t = tot+1;
for(int i = 0; i <= t; ++i) head[i] = -1, vis[i] = 0;
for(int i = 1; i <= n; ++i) {
if(!vis[p[i].lx]) Add(s, p[i].lx, 1), vis[p[i].lx] = 1;
Add(p[i].lx, p[i].ly, 1);
if(!vis[p[i].ly]) Add(p[i].ly, t, 1), vis[p[i].ly] = 1;
}
dinic();
}
return 0;
}