2020 杭电多校4 1007 Go Running

题目链接
以x与t为轴建立直角坐标系,可以发现向右跑的同一个人在 x=t+b这条直线上,向左跑的同一个人在x=-t+b这条直线线上,肯定是让同一条直线上人算做一个人才会最少,但是直线的交点怎么算是个问题。当时还真想不到怎么建图跑网络流太菜了。
对于每个点都会有一个 xi+ti 和一个 xi-ti,在他们之间连一条有向边,最终就会建成一个二分图,问题就变成了求最小点覆盖。对于有向图最小点覆盖等于最大匹配,对于无向图还要除以二。再建造一个超级源点和一个超级汇点可以跑网络流了。xi和ti的值太大还要离散化一下。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000010,inf=0x3f3f3f3f;
int _,s,t,n,m,h[N],e[N],ne[N],w[N],idx;
int dep[N],cur[N];
bool st[N];
int cnt,cnt2;
unordered_map<ll,int> mp1,mp2;
void add(int a,int b,int c)
{
    e[idx]=b;ne[idx]=h[a];w[idx]=c;h[a]=idx++;
    e[idx]=a;ne[idx]=h[b];w[idx]=0;h[b]=idx++;
}
bool bfs()
{
    for(int i=0;i<=cnt;i++) dep[i]=inf;
    dep[t]=inf;
    dep[s]=1;
    queue<int> q;
    q.push(s);
    while(q.size())
    {
        int u=q.front();
        q.pop();
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int v=e[i];
            if(dep[v]==inf&&w[i]>0)
            {
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    return dep[t]!=inf;
}
int dfs(int u,int flow)
{
    if(u==t) return flow;
    int sum=0;
    for(int i=cur[u];i!=-1;i=ne[i])
    {
        cur[u]=i;
        int v=e[i];
        if(dep[v]==dep[u]+1&&w[i]>0)
        {
            int tmp=dfs(v,min(flow,w[i]));
            w[i]-=tmp;
            w[i^1]+=tmp;
            sum+=tmp;
            flow-=tmp;
            if(!flow) break;
        }
    }
    return sum;
}
int dinic()
{
    int flow=0;
    while(bfs())
    {
        for(int i=0;i<=cnt;i++) cur[i]=h[i];
        cur[t]=h[t];
        flow+=dfs(s,inf);
    }
    return flow;
}
int main()
{
    scanf("%d",&_);
    while(_--)
    {
        mp1.clear();mp2.clear();
        scanf("%d",&n);
        s=0;
        t=2*n+1;//最多会拆出2n个点,汇点建在2n+1不影响
        for(int i=0;i<=n*2+1;i++) h[i]=-1;
        idx=cnt=0;
        for(int i=1;i<=n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            int x=a-b,y=a+b;
            if(mp1.count(x)==0)
            {
                mp1[x]=++cnt;
                add(s,mp1[x],1);
            }
            if(mp2.count(y)==0)
            {
                 mp2[y]=++cnt;
                 add(mp2[y],t,1);
            } 
            add(mp1[x],mp2[y],1);
        }
        printf("%d\n",dinic()); 
    }    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值