您的位置:首页 > 其它

bzoj 1104 贪心+并查集

2017-04-12 20:00 211 查看
题意: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



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bzoj