[最小生成树]清扫

清扫

时间限制: 1 Sec 内存限制: 128 MB

题目描述

现在要打扫国王的牧圈。已经30年没打扫了。所以这次的计划是用河水来冲。
牧圈排成整齐的格子,每相邻的两个之间都有门。要想让水进去,就必须打开这些门。这不是件容易的事情。因为有些圈里土堆得很高。因此打开门就很费劲。为了使花的力气最小,总是把门推向土低的一边。你的任务是计算最少得费多少劲。我们用土的厚度来描述这个值。

输入

第一行是宽度w和高度h,其中3 <= w,h <= 40。以下h行数据,描述了土的高度,也就是我们所浪费体力的度量。数据的范围在1到100之间。

输出

你得到的结果。所有的格子都必须进水。水是从左上角的格子进去的。

样例输入

4 3
3 5 2 1
7 3 4 8
1 6 5 7

样例输出

26
Alt text

题解

给出了各个点的土堆高度,可以得出相邻两个点间的花费,即分w[i,j]=min(x[i],x[j])
然后Prim/Kruskal随便搞了

Kruskal版本

var
 rank,father:array[0..1600]of longint;
 edge:array[0..4950,1..3]of longint;
 w:array[1..40,1..40]of longint;
 i,j,k,l:longint;
 n,m,ans:longint;
 a,b,c,d:longint;
procedure sort(l,r: longint);
var i,j,k,x,y: longint;
begin
 i:=l; j:=r; x:=edge[(l+r) div 2,3];
 repeat
  while edge[i,3]<x do inc(i);
  while x<edge[j,3] do dec(j);
  if not(i>j)
  then
   begin
    for k:=1 to 3 do
     begin y:=edge[i,k]; edge[i,k]:=edge[j,k]; edge[j,k]:=y; end;
    inc(i); dec(j);
   end;
 until i>j;
 if l<j then sort(l,j);
 if i<r then sort(i,r);
end;

function getfather(a:longint):longint;
begin
 if father[a]=a
 then exit(a);
 father[a]:=getfather(father[a]);
 exit(father[a]);
end;

procedure union(a,b:longint);
var x,y,tt:longint;
begin
 x:=getfather(a);
 y:=getfather(b);
 tt:=rank[x]+rank[y];
 if rank[x]>rank[y]
 then begin rank[x]:=tt; rank[y]:=rank[x]; father[y]:=x; getfather(b); end
 else begin rank[y]:=tt; rank[x]:=rank[y]; father[x]:=y; getfather(a); end;
end;

function min(a,b:longint):longint;
begin
 if a>b
 then exit(b)
 else exit(a);
end;

begin
 readln(n,m);
 for i:=1 to m do
  begin
   for j:=1 to n do
    read(w[i,j]);
   readln;
  end;
 for i:=1 to m do
  for j:=1 to n do
   begin
    k:=1; l:=0;
      if (i+k>0)and(i+k<=m)and(j+l>0)and(j+l<=n)
      then
       begin
        inc(edge[0,1]);
        edge[edge[0,1],1]:=(i+k-1)*n+j+l;
        edge[edge[0,1],2]:=(i-1)*n+j;
        edge[edge[0,1],3]:=min(w[i,j],w[i+k,j+l]);
       end;
    k:=0; l:=1;
      if (i+k>0)and(i+k<=m)and(j+l>0)and(j+l<=n)
      then
       begin
        inc(edge[0,1]);
        edge[edge[0,1],1]:=(i+k-1)*n+j+l;
        edge[edge[0,1],2]:=(i-1)*n+j;
        edge[edge[0,1],3]:=min(w[i,j],w[i+k,j+l]);
       end;
   end;
 sort(1,edge[0,1]);
 for i:=1 to edge[0,1] do
  begin
   if edge[i,1]>edge[i,2]
   then begin d:=edge[i,1]; edge[i,1]:=edge[i,2]; edge[i,2]:=d; end;
  end;
 for i:=1 to n*m do
  begin
   father[i]:=i;
   rank[i]:=1;
  end;
 for i:=1 to edge[0,1] do
  begin
   if getfather(edge[i,1])<>getfather(edge[i,2])
   then
    begin
     union(edge[i,1],edge[i,2]);
     inc(ans,edge[i,3]);
    end;
  end;
 writeln(ans);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值