三条线_纪中_2929_最大匹配

题目大意

  为了监视他的N (1 <= N <= 50,000)头奶牛,Farmer John购买了新的监视系统。第i头奶牛位置在(x_i, y_i),坐标为整数,范围0..1,000,000,000。任意两头奶牛的位置不同。   FJ的监视系统有三个摄像头,每个摄像头只能监视一条水平线或者垂直线上的所有奶牛。请计算如果FJ安装好这三个摄像头,能否监视所有的N头奶牛。也就是说,计算N头奶牛的位置,是否可以被三条直线“覆盖”,直线必须是水平线或垂直线。


分析

  最小覆盖问题: 给定一个二分图,求最小的点数,使得每一条边都至少与一个顶点相邻。 

  可以证明:二分图最小覆盖数=最大匹配数。 

  证明:假设最大匹配边数为M。

   1.M是足够的。因为如果存在边E未与顶点相连,则E可以匹配,此时不是最大匹配。 

   2.M是必须的。仅考虑构成最大匹配的M条边,他们两两无公共点,所以需要M个顶点覆盖他们。 

  看不懂?好吧。简而言之,对于i行j列有按钮的格子连边i->j,然后求最大匹配数。

  记得用离散,因为坐标很大。

  我和olahiuj——就是他

  都想了正解,但是他机(sha)智(bi)的算错了最大匹配数(蜜汁统计错误)。


代码

type
  arr=record
    x,y,w:longint;
    next:longint;
end;

type
  arry=array[0..50001,1..2] of longint;
var
  a:array[0..50001] of arr;
  v:array[0..50001] of boolean;
  st,ls:array[0..50001] of longint;
  x,y,xy:array[0..50001,1..2] of longint;
  i,j:longint;
  n,m,nm,n1,m1:longint;
  k:char;

function find(r:longint):boolean;
var
  i,j,k:longint;
begin
  find:=true;
  i:=ls[r];
  while i<>0 do
    begin
      with a[i] do
        if not v[y]
          then
            begin
              k:=st[y]; st[y]:=r; v[y]:=true;
              if (k=0) or find(k) then exit;
              st[y]:=k;
            end;
      i:=a[i].next;
    end;
  find:=false;
end;

procedure main;
var
  i,j,k:longint;
  z:boolean;
begin
  for i:=1 to n1 do
    begin
      fillchar(v,sizeof(v),0);
      z:=find(i);
    end;
end;

procedure add(x,y:longint);
begin
  nm:=nm+1;
  a[nm].x:=x;
  a[nm].y:=y;
  a[nm].next:=ls[x];
  ls[x]:=nm;
end;

procedure qsort(var a:arry;l,r:longint);
var
  i,j,k:longint;
  mid:longint;
  temp:longint;
begin
  if (l>=r) then exit;
  i:=l; j:=r;
  mid:=a[(l+r) div 2][1];
  repeat
    while a[i][1]<mid do i:=i+1;
    while a[j][1]>mid do j:=j-1;
    if i<=j
      then
        begin
          temp:=a[i][1]; a[i][1]:=a[j][1]; a[j][1]:=temp;
          temp:=a[i][2]; a[i][2]:=a[j][2]; a[j][2]:=temp;
          i:=i+1; j:=j-1;
        end;
  until i>j;
  qsort(a,l,j);
  qsort(a,i,r);
end;

begin
  fillchar(ls,sizeof(ls),0);
  fillchar(st,sizeof(st),0);
  fillchar(v,sizeof(v),0);
  readln(n);
  nm:=0;
  for i:=1 to n do
    begin
      readln(x[i][1],y[i][1]);
      x[i][2]:=i;
      y[i][2]:=i;
    end;
  qsort(x,1,n);
  qsort(y,1,n);
  j:=1;
  xy[x[1][2]][1]:=j;
  for i:=2 to n do
    begin
      if x[i-1][1]<>x[i][1] then j:=j+1;
      xy[x[i][2]][1]:=j;
    end;
  n1:=j;
  j:=1;
  xy[y[1][2]][2]:=j;
  for i:=2 to n do
    begin
      if y[i-1][1]<>y[i][1] then j:=j+1;
      xy[y[i][2]][2]:=j;
    end;
  m1:=j;
  for i:=1 to n do
    add(xy[i][1],xy[i][2]);
  main;
  j:=0;
  for i:=1 to m1 do
    if st[i]<>0 then j:=j+1;
  if j<=3
    then write(1)
    else write(0);
end.



转载于:https://www.cnblogs.com/a-loud-name/p/6184777.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值