2020杭电多校赛第四场1007 Go Running(hdu6808)

该博客详细介绍了HDU 1007 Go Running问题的解题思路。通过建立坐标系,将问题转化为寻找斜率为1或-1的直线,进一步转为二分图匹配问题,利用网络流算法求解最小点覆盖。博主通过顺时针旋转45度简化问题,并提供了具体的网络流建模和代码实现。
摘要由CSDN通过智能技术生成

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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值