拦截导弹 题解

Description

  GGF最近经常遭到不明导弹的袭击,因此他只好开发了一套导弹拦截系统。
  该系统非常先进,使用会拐弯的激光对导弹进行射击。-_-
  所有被激光击中的导弹就瞬间完蛋,而且一束激光可以同时摧毁多枚导弹。
  但激光只能向上发射,所以若同一束激光能依次击中多枚导弹,则这些导弹坐标的每一维都严格单调上升。建立了一个三维坐标系后:
  假设4枚导弹坐标为:(0, 0, 0) (1, 1, 0) (1, 1, 1), (2, 2, 2)
  则一束激光可以依次击毁1,3,4号导弹,却不能依次击毁1,2,4,也不能依次击毁4,3,1。
  现在,GGF发现N枚导弹正向自己飞来。-_-
  在定位了导弹的坐标后,他想知道,只发射一束激光最多可以摧毁多少枚导弹,若要摧毁所有导弹,至少需要发射多少束激光。(激光速度极快,所以可以认为导弹在那一瞬间静止,而且激光间不相互影响)

Input

  输入文件第一行一个正整数N(1<=N<=2000)。
  下面N行,每行3个整数Xi,Yi,Zi(0<=Xi,Yi,Zi<=10^6) ,描述一个导弹的坐标。

Output

  两行,第一行一个整数表示最多可以拦截多少枚导弹。
  第二行一个整数表示最少发射多少束激光才能拦截全部导弹。

Sample Input

4
1 2 3
1 1 1
2 5 4
0 1 1

Sample Output

3
2

Data Constraint

Hint

【数据规模】
  对于20%的数据,有N<=10。
  对于50%的数据,有N<=300。
  对于100%的数据如题。

题解

发现导弹的打击关系可以变成一个无环dag
那么最多打多少导弹就是求dag中的最长路径。
多少套系统就是求最小路径覆盖。

最小路径覆盖=n-最大匹配。

所以跑一遍bfs和匈牙利即可。

type aa=record
  x,y,z:longint;
end;
var n,i,j,k,l,tot,tot1,ans:longint;
d,head,head1,f,bz,g,r:array[0..100000]of longint;
next,next1,t,t1:array[0..4000000]of longint;
a:array[0..100000]of aa;
procedure sort(l,r:longint);
var i,j,kx,ky,kz:longint;
begin
  i:=l;
  j:=r;
  kx:=a[(l+r)div 2].x;
  ky:=a[(l+r)div 2].y;
  kz:=a[(l+r)div 2].z;
  repeat
    while (a[i].x<kx)or((a[i].x=kx)and(a[i].y<ky))or((a[i].x=kx)and(a[i].y=ky)and(a[i].z<kz)) do inc(i);
    while (a[j].x>kx)or((a[j].x=kx)and(a[j].y>ky))or((a[j].x=kx)and(a[j].y=ky)and(a[j].z>kz)) do dec(j);
    if i<=j then begin
      a[0]:=a[i];
      a[i]:=a[j];
      a[j]:=a[0];
      inc(i);
      dec(j);
    end;
  until i>j;
  if i<r then sort(i,r);
  if j>l then sort(l,j);
end;
procedure ad(x,y:longint);
begin
  inc(tot);
  next[tot]:=head[x];
  head[x]:=tot;
  t[tot]:=y;
end;
procedure ad1(x,y:longint);
begin
  inc(tot1);
  next1[tot1]:=head1[x];
  head1[x]:=tot1;
  t1[tot1]:=y;
end;
procedure bfs;
var i,j,k,l:longint;
begin
  k:=0;
  for i:=1 to n do if r[i]=0 then begin
    inc(k);
    d[k]:=i;
    f[i]:=1;
  end;
  i:=0;
  while i<k do begin
    inc(i);
    j:=head[d[i]];
    while j<>0 do begin
      f[t[j]]:=f[d[i]]+1;
      dec(r[t[j]]);
      if r[t[j]]=0 then begin
        inc(k);
        d[k]:=t[j];
      end;
      j:=next[j];
    end;
  end;
  ans:=f[d[n]];
end;
function dfs(x:longint):longint;
var j:longint;
begin
  if bz[x]=i then exit(0);
  bz[x]:=i;
  j:=head1[x];
  while j<>0 do begin
    if (g[t1[j]]=0)or(dfs(g[t1[j]])=1) then begin
      g[t1[j]]:=x;
      exit(1);
    end;
    j:=next1[j];
  end;
  exit(0);
end;
begin
  readln(n);
  for i:=1 to n do readln(a[i].x,a[i].y,a[i].z);
  sort(1,n);
  for i:=2 to n do begin
    for j:=1 to i-1 do begin
      if (a[i].x>a[j].x)and(a[i].y>a[j].y)and(a[i].z>a[j].z) then begin
        ad(j,i);
        inc(r[i]);
        ad1(j,i+n);
      end;
    end;
  end;
  bfs;
  writeln(ans);
  ans:=0;
  for i:=1 to n do ans:=ans+dfs(i);
  writeln(n-ans);
end.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值