杭电第四场 Go Running 二分图

Go Running

题目链接

题目大意

一些人在跑步,从任意一个位置开始,向左或向右跑,速度都为1 m/s。
现在已经知道在ti 时刻xi位置上出现了至少一个人,问最少有多少人在跑步

题解

先说一说比赛的时候的不足:
想到了往右跑的人在任意时刻 t - x 一定是相等的。
想到了往左跑的人在任意时刻 t + x 一定是相等的。
现在就是找一些值覆盖这n个点。。
然后就不会了,感觉有点像二分图,但是一直不知道怎么搞,像是脑子死机了一样。还有对时间复杂度理解的不够,一直认为1e5跑网络流会tle,可是二分图上的网络流复杂度为n * sqrt(n) ;
然后这道题就出不来了。总之:我好菜
其实再给t - x 与t + x连边,找个最小点覆盖(最大匹配)就好了。。
现在才是真的题解
可以以ti为横坐标xi为纵坐标把点表示在二维坐标系上,然后可以看出一个点往右走会延伸出一条斜率为1的直线,往左走会有一条斜率为-1的直线覆盖。
然后 找最少的斜率为1或-1的直线覆盖所有的点就好了。。
然后把坐标系旋转一下,会变成选一些横着的线和竖着的线覆盖所有点,然后就是找一些x,y覆盖所有的点。可以每个点的x和y连边跑最小覆盖数。
二分图里 最小覆盖数 = 最大匹配数
然后跑个最大匹配就好。
这个跟上面的理论其实一样的。。
怎么找覆盖问题,,是我做的题太少了,
代码:

#include<algorithm>
#include<cstring>
#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
typedef unsigned long long ull;
typedef set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void wenjian(){freopen("concatenation.in","r",stdin);freopen("concatenation.out","w",stdout);}
void tempwj(){freopen("hash.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second < b.second;}};
int lb(int x){return  x & -x;}
//friend bool operator < (Node a,Node b)   重载
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int maxn = 5e5+10;
pii pp[maxn];
std::vector<int> v1;
std::vector<int> v2;
int getid1(int x)
{
	return lower_bound(v1.begin(),v1.end(),x) - v1.begin() + 1;
}
int getid2(int x)
{
	return lower_bound(v2.begin(),v2.end(),x) - v2.begin() + 1;
}
struct Edge
{
	int id,val,nex;
}edge[maxn << 2];
int cnt;
int head[maxn];
void add(int x,int y,int val)
{
	edge[cnt].id = y;
	edge[cnt].val = val;
	edge[cnt].nex = head[x];
	head[x] = cnt ++ ;
}
int n,m,s,t;
int dep[maxn];
queue<int> qq;
bool bfs()
{
	for (int i = 1; i <= n; i ++ )
	{
		dep[i] = 0;
	}
	qq.push(s);
	dep[s] = 1;
	while(!qq.empty())
	{
		int x = qq.front();
		qq.pop();
		for (int i = head[x]; ~i; i = edge[i].nex)
		{
			int v = edge[i].id;
			if(edge[i].val && dep[v] == 0)
			{
				dep[v] = dep[x] + 1;
				qq.push(v);
			}
		}
	}
	// printf("%d\n",dep[t]);
	if(dep[t] == 0)
		return false;
	return true;
}

int dinic(int x,int fl)
{
	if(x == t)
		return fl;
	int s = 0;
	for (int i = head[x]; ~i; i = edge[i].nex)
	{
		int v = edge[i].id;
		if(edge[i].val && dep[v] == dep[x] + 1)
		{
			int p = dinic(v,min(fl,edge[i].val));
			s += p;
			edge[i].val -= p;
			edge[i^1].val += p;
			fl -= p;
		}
	}
	if(s == 0)
	{
		dep[x] = 0;
		return 0;
	}
	else
		return s;
}
int getans()
{
	int ans = 0;
	while(1)
	{
		if(!bfs())
			break;
		// printf("%d\n", );
		ans += 1ll * dinic(s,inf);
	}
	return ans;
}


int main()
{
	int T;
	scanf("%d",&T);
	while(T -- )
	{
		v1.clear();
		v2.clear();
		int nn;
		scanf("%d",&nn);
		for (int  i= 1; i <= nn; i ++ )
		{
			int x,y;
			scanf("%d%d",&x,&y);
			pp[i].st = x + y;
			pp[i].sd = x - y;
			v1.pb(pp[i].st);
			v2.pb(pp[i].sd);
		}
		sort(v1.begin(),v1.end());
		v1.erase(unique(v1.begin(),v1.end()),v1.end());
		int m = v1.size();
		sort(v2.begin(),v2.end());
		v2.erase(unique(v2.begin(),v2.end()),v2.end());
		int m2 = v2.size();
		n = m + m2;
		s = m + m2 + 1;
		t = m + m2 + 2;
		n += 2;
		for (int  i= 1; i <= n; i ++ )
			head[i] = -1;
		cnt = 0;
		for(int i =1 ; i <= m; i ++ )
		{
			add(s,i,1);
			add(i,s,0);
		}
		for(int i = 1; i <= nn; i ++ )
		{
			int x = getid1(pp[i].st);
			int y = getid2(pp[i].sd) + m;
			// printf("%d %d\n",x,y);
			add(x,y,1);
			add(y,x,0);
		}
		for(int i = m + 1; i <= m + m2; i ++ )
		{
			add(t,i,0);
			add(i,t,1);
		}
		printf("%d\n",getans());
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值