2020杭电多校第四场 Go Running(二分图最小点覆盖)

题目链接
2020杭电多校第四场 Go Running(二分图最小点覆盖)_第1张图片
比赛时一直以为是贪心题,结果一直被卡死, 只能说这应该是一道非常经典的二分图问题。
将每个点放在坐标轴上,即(t, pos+vt),而已知v为1,而在同一条斜率为1或-1的直线上的点可能都是同一个学生,所以这就转化成了非常经典的最小点覆盖问题,最少用多少条斜率固定线可以覆盖所有的点。
同时,我们可以把经过每个点的斜率为1和-1的直线与坐标轴的交点求出来,因为数据很大,记得离散化处理。因为最小点覆盖就等于二分图的最大匹配 (证明略) ,所以只要跑一遍匈牙利算法就ok了。

怕被卡时间,所以用了最大流优化了一下。

#include 
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 5e5 + 10;

vector<int> v1, v2;

struct edge {
	int to, next, cap;
}e[N*2];

int T, n, m, cnt, h[N], maxflow, s, t;
int x[N], y[N], vis[N], match[N], dep[N], cur[N];

void add(int u, int v, int cap) {
	e[cnt].to = v;
	e[cnt].cap = cap;
	e[cnt].next = h[u];
	h[u] = cnt++;

	e[cnt].to = u;
	e[cnt].cap = 0;
	e[cnt].next = h[v];
	h[v] = cnt++;
}

bool bfs() {
	for (int i = 0; i <= t; i++) dep[i] = -1, cur[i] = h[i];

	queue<int> q;
	q.push(s);
	dep[s] = 0;

	while (q.size()) {
		int u = q.front();
		q.pop();
		for (int i = h[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (e[i].cap && dep[v] == -1) {
				dep[v] = dep[u] + 1;
				q.push(v);
			}
		}
	}
	if (dep[t] >= 0) return true;
	else return false;
}

int dfs(int u, int mx) {
	int a;
	if (u == t) return mx;
	for (int i = cur[u]; ~i; i = e[i].next) {
		cur[u] = i;
		int v = e[i].to;
		if (e[i].cap && dep[v] == dep[u] + 1 && (a = dfs(v, min(e[i].cap, mx)))) {
			e[i].cap -= a;
			e[i ^ 1].cap += a;
			return a;
		}
	}
	return 0;
}

void dinic() {
	int res;
	while (bfs()) {
		while (1) {
			res = dfs(s, INF);
			if (!res) break;
			maxflow += res;
		}
	}
}


void init() {
	v1.clear();
	v2.clear();
	maxflow = 0;
	cnt = 0;
}
int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		init();
		for (int i = 0; i < n; i++) {
			int a, b;
			scanf("%d %d", &a, &b);
			x[i] = a + b, y[i] = a - b;
			v1.push_back(x[i]), v2.push_back(y[i]);
		}
		sort(v1.begin(), v1.end());
		v1.erase(unique(v1.begin(), v1.end()), v1.end());

		sort(v2.begin(), v2.end());
		v2.erase(unique(v2.begin(), v2.end()), v2.end());
		
		int len = v1.size();
		s = 0, t = v1.size() + v2.size() + 1;
		for (int i = 0; i <= t; i++) h[i] = -1;
		for (int i = 1; i <= len; i++) add(s, i, 1);
		for (int i = 0; i < n; i++) {
			y[i] = lower_bound(v2.begin(), v2.end(), y[i]) - v2.begin() + 1;
			x[i] = lower_bound(v1.begin(), v1.end(), x[i]) - v1.begin() + 1;
			add(x[i], y[i] + len, 1);
		}
		for (int i = len+1; i <= t-1; i++) add(i, t, 1);
		dinic();
		printf("%d\n", maxflow);
	}
}

你可能感兴趣的:(图论,二分图,算法)