bzoj 1104 贪心+并查集

25 篇文章 0 订阅
15 篇文章 0 订阅

题意:n*m的网格,所有格子都被水淹没,给定一些关键点,求用最少的抽水机抽干所有关键点的水(水的流动遵循连通器原理)

我觉得这道题的真名应该叫做 “被水淹没,不知所措”,考试的时候自己YY出来的好棒棒= =,

蛮好的一道题

经过贪心可知,

(1)一定存在一种最优解使所有的水泵都在关键点上

(2)由于水都是从高往低留,所以一定会优先放置高度低的关键点

那么我们将所有关键点按照高度排序,从小到大枚举每个关键点

考虑,如果我们在格子a放了个水泵,那么当且仅当a到b存在一条路径满足这条路径上的高度全都小于等于格子b的高度时,a处的水泵能抽干b

那么我们可对于一个格子i,我们找到它周围所有高度都不大于它的格子,与i合并

为什么可以合并呢?

因为这些格子中,只要任意一个放了水泵,那么i就可以被抽干

如果格子i是关键点,并且当i和周围所有高度不大于它的格子全都合并后没有任何一个集合是放过水泵的,那么我们就把答案+1并把这个集合置为真(真表示这个集合放过水泵,即集合里的所有点都可以被抽干)

整理一下思路,

我们需要两次排序,一次是将所有关键点按高度从小到大排序,一次是把所有点按高度排序

从小到大枚举关键点,

当我们判断一个关键点x是否需要新加一个水泵的时候,需要把地图上所有高度小于等于该点高度的格子都按上述合并,即把它和它周围高度小于等于它的合并(相当于一个预处理,如果对于每个关键点x现顺着捯做bfs的话,可能会T,也可能是蒟蒻写丑了)

并查集维护的是同组信息(可以共用一个水泵的点集),在合并的时候,如果任意一个集合的标记为真,则合并后的的标记为真

找到x所在集合,判断标记是否为真,如果是假,则答案+1并标记置为真

type
        rec=record
            x,y,h:longint;
end;

const
        way:array[1..4,1..2] of integer=((1,0),(0,1),(-1,0),(0,-1));
var
        n,m,ans,tx,ty,tot:longint;
        i,j,k            :longint;
        map              :array[0..1010,0..1010] of longint;
        city,a           :array[0..1000010] of rec;
        f                :array[0..1000010] of longint;
        flag             :array[0..1000010] of boolean;
function get_father(x:longint):longint;
begin
   if x=f[x] then exit(x);
   f[x]:=get_father(f[x]);
   exit(f[x]);
end;

procedure connect(x,y:longint);
begin
   x:=get_father(x);
   y:=get_father(y);
   if x<>y then
   begin
     f[y]:=x;
     if flag[x] or flag[y] then flag[x]:=true
       else flag[x]:=false;
   end;
end;

procedure sort1(l,r:longint);
var
        i,j,x:longint;
        y:rec;
begin
   i:=l; j:=r; x:=city[(l+r) >>1].h;
   while (i<=j) do
   begin
      while city[i].h<x do inc(i);
      while city[j].h>x do dec(j);
      if (i<=j) then
      begin
         y:=city[i]; city[i]:=city[j]; city[j]:=y;
         inc(i); dec(j);
      end;
   end;
   if i<r then sort1(i,r);
   if j>l then sort1(l,j);
end;

procedure sort2(l,r:longint);
var
        i,j,x:longint;
        y:rec;
begin
   i:=l; j:=r; x:=a[(l+r) >>1].h;
   while (i<=j) do
   begin
      while a[i].h<x do inc(i);
      while a[j].h>x do dec(j);
      if (i<=j) then
      begin
         y:=a[i]; a[i]:=a[j]; a[j]:=y;
         inc(i); dec(j);
      end;
   end;
   if i<r then sort2(i,r);
   if j>l then sort2(l,j);
end;

begin
   //assign(input,'pump.in');reset(input);
   //assign(output,'pump.out');rewrite(output);
   read(n,m);
   for i:=1 to n*m do f[i]:=i;
   for i:=1 to n do
     for j:=1 to m do
     begin
        read(map[i,j]);
        if  map[i,j]>0 then
        begin
           inc(tot);
           city[tot].x:=i;
           city[tot].y:=j;
           city[tot].h:=map[i,j];
        end else map[i,j]:=-map[i,j];
        a[(i-1)*m+j].x:=i;
        a[(i-1)*m+j].y:=j;
        a[(i-1)*m+j].h:=map[i,j];
     end;
   sort1(1,tot);
   sort2(1,n*m);
   //
   j:=1;
   for i:=1 to tot do
   begin
      while (j<=n*m) and (a[j].h<=city[i].h) do
      begin
         for k:=1 to 4 do
         begin
            tx:=a[j].x+way[k,1];
            ty:=a[j].y+way[k,2];
            if (tx>0) and (ty>0) and (tx<=n) and (ty<=m)
              and (map[tx,ty]<=map[a[j].x,a[j].y])
                then connect((a[j].x-1)*m+a[j].y,(tx-1)*m+ty);
         end;
         inc(j);
      end;
      if not flag[get_father((city[i].x-1)*m+city[i].y)] then
      begin
         inc(ans);
         flag[get_father((city[i].x-1)*m+city[i].y)]:=true;
      end;
   end;
   writeln(ans);
 //  close(input);close(output);
end.
——by Eirlys


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值