[hdu-6808] Go Running 最小点覆盖 网络流 2020多校4

【题目链接】http://acm.hdu.edu.cn/showproblem.php?pid=6808

【题目大意】

坐标轴上,人可以选择自己开始跑步的时间、位置、方向(正方向/负方向)

有n个观测点给出观测时间和坐标,此处此时至少有一个人跑步,问最少有几个人跑步


转变为选最少的直线(斜率为1或-1)覆盖坐标中的点
可以把坐标旋转45°,转变为画平行于x或y轴的直线覆盖所有点
把点的x坐标和y坐标分别作为二分图中两侧的点,连边
转变为二分图最小点覆盖的问题 。最小点覆盖=二分图最大匹配 

ps:数据量太大,需要用dinic跑最大流

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 int const maxn=2e5+7,maxn2=1e5+7;;
  4 typedef long long ll;
  5 const ll inf=1e18;
  6 int s,t,n,id1[maxn],id2[maxn],fr[maxn2],to[maxn2],numid1,numid2,tot;
  7 int cur[maxn],head[maxn],deep[maxn];
  8 void init(){
  9     memset(id1,0,sizeof(id1));
 10     memset(id2,0,sizeof(id2));
 11     memset(head,-1,sizeof(head));
 12     numid1=0;
 13     numid2=0;
 14     tot=-1;
 15 }
 16 struct edge{
 17     int to,nxt;
 18     ll flow;
 19 }e[maxn*6];
 20 void build(int x,int y,int z){
 21     e[++tot].to=y;
 22     e[tot].flow=z;
 23     e[tot].nxt=head[x];
 24     head[x]=tot;
 25     // printf("%d : %d %d \n",tot,x,y,z);
 26 }
 27 //二分图反相边
 28 void build_dinci(int x,int y,int z){
 29     e[++tot].to=y;e[tot].flow=z;e[tot].nxt=head[x];head[x]=tot;
 30     e[++tot].to=x;e[tot].flow=0;e[tot].nxt=head[y];head[y]=tot;
 31 }
 32 queue<int>q;
 33 int bfs(){
 34     while(!q.empty())q.pop();
 35     memset(deep,0,sizeof(deep));
 36     deep[s]=1;
 37     q.push(s);
 38     while(!q.empty()){
 39         int u=q.front();q.pop();
 40         for(int i=head[u];i!=-1;i=e[i].nxt){
 41             if(e[i].flow&&!deep[e[i].to]){//要还有流量
 42                 deep[e[i].to]=deep[u]+1;
 43                 q.push(e[i].to);
 44             }
 45         }
 46     }
 47     return deep[t];
 48 }
 49 ll    dfs(int u,ll limit){
 50     if(u==t)return limit;
 51     for(int &i=cur[u];i!=-1;i=e[i].nxt){//当前弧优化  i是cur[i],下一个还没被访问过的边
 52        //int& 是引用,变量别名,修改i就修改了cur[u]
 53         int v=e[i].to;
 54         if(deep[v]==deep[u]+1&&e[i].flow){//u只能由deep[u]-1到达
 55             ll nowdi=dfs(v,min(limit,e[i].flow));
 56             if(nowdi){
 57                 e[i].flow-=nowdi;
 58                 e[i^1].flow+=nowdi;
 59                 return nowdi;
 60             }
 61         }
 62     }
 63     return 0;
 64 }
 65 ll  dinic(){
 66     ll  ans=0;
 67     while(bfs()){
 68         for(int i=1;i<=n;i++)//bfs分层
 69             cur[i]=head[i];
 70         ll d=dfs(s,inf);
 71         while(d){
 72             ans+=d;//寻找增广路
 73             d=dfs(s,inf);
 74         }
 75     }
 76     return ans;
 77 }
 78 int main(){
 79     int T,a,b;
 80     scanf("%d",&T);
 81     while(T--){
 82         scanf("%d",&n);
 83         init();
 84         for(int i=1;i<=n;i++){
 85             scanf("%d%d",&a,&b);
 86             fr[i]=b-a+1e5,to[i]=b+a;
 87             //把图旋转45°,转化成画平行于x或者y轴的线,y-x   y+x
 88             if(!id1[fr[i]])id1[fr[i]]=++numid1;
 89             if(!id2[to[i]])id2[to[i]]=++numid2;
 90         }
 91 /*planb
 92 先存下来,然后离散化(用时间换空间)
 93 (此处用空间换时间)
 94 
 95 题意概述:
 96 人可以选择自己开始跑步的位置和方向,问最少有几个人跑步
 97 转变为选最少的直线(斜率为1或-1)覆盖坐标中的点
 98 可以把坐标旋转45°,转变为画平行于x或y轴的直线覆盖所有点
 99 
100 把点的x坐标和y坐标分别作为二分图中两侧的点,连边
101 转变为二分图最小点覆盖的问题 。最小点覆盖=二分图最大匹配  ,数据量太大,需要用dinic跑最大流
102 */
103         s=numid1+numid2+1,t=s+1;
104         for(int i=1;i<=n;i++){
105             build_dinci(id1[fr[i]],id2[to[i]]+numid1,1);
106         }
107         //注意二分图两边的点,   只和源点/汇点 加一次!!
108         for(int i=1;i<=numid1;i++){
109             build_dinci(s,i,1);
110         }
111         for(int i=1;i<=numid2;i++){
112             build_dinci(i+numid1,t,1);
113         }
114         n=numid1+numid2+2;
115         ll ans=dinic();
116         printf("%lld\n",ans);
117         
118     }
119     return 0;
120 }

 

posted @ 2020-10-11 11:53  conver^_^  阅读(136)  评论(0编辑  收藏  举报