poj 2227 & usaco 2005 月赛 The Wedding Juicer 题解 bfs+优先队列

本文详细解析了一道经典的矩阵盛水问题,通过巧妙地利用优先队列和小根堆进行边界到内部的广度优先搜索,实现了高效求解最多能存放多少单位水的算法。

参考http://blog.csdn.net/jiangshibiao/article/details/24132503
题目链接:https://vjudge.net/problem/POJ-2227

【题意】给定N*M的一个矩阵表示某一格的高度,求最多能放多少单位的水。自然,水会漫延。

【分析】这道题出的挺好。我觉得这不是考验代码能力(P党就无视吧),重在思维。(哇哈哈,其实我也是看题解的)原来的思想是二分枚举水量或高度,但是发现行不通。于是我自然的想到了bfs,,而且想到从边界向中间bfs。但是究竟该怎么控制呢?主要是有一种情况难以考虑:假设边界的最低点是5,但是中间有一个凸起,中间一圈都是7,那么中间的中间最多可以盛7单位。我们可以先把边界上的点全都加入优先队列,同时维护一个小根堆。整个堆的意义是:已堆中元素为边界去盛水。然后我们找到堆首元素并向四个方向拓展。

①如果某个点比当前的点高,那么可以把他也加入优先队列中。

②如果某个点比当前的点低,那么我就可以多盛(h-h')单位的水(两个高度差)。为什么呢?因为我当前堆首已经满足是整个边界中的最小值了;这样一点水不可能从任何其他边界出去。更新答案后,我们再把拓展出去的点的高度标为当前的高度h并加入优先队列(因为这个点以后可能还能盛水)。

直到队列为空,我们已经拓展了所有可能的点了。
#include<cstdio>
#include<queue>
using namespace std;
struct arr
{
  int x,y,h;
  friend bool operator < (const arr &a,const arr &b)
  {
    return  a.h>b.h;
  }
}temp;
const int dx[4]={0,0,-1,1};const int dy[4]={1,-1,0,0};
priority_queue<arr>q;
int n,m,i,j,x,y,xx,yy,ans,a[305][305];
bool visit[305][305];
int main()
{
  scanf("%d%d",&m,&n);
  for (i=1;i<=n;i++)
    for (j=1;j<=m;j++)
      scanf("%d",&a[i][j]);
  for (i=1;i<=m;i++)
    q.push((arr){1,i,a[1][i]}),q.push((arr){n,i,a[n][i]}),visit[1][i]=visit[n][i]=true;
  for (i=2;i<n;i++)
    q.push((arr){i,1,a[i][1]}),q.push((arr){i,m,a[i][m]}),visit[i][1]=visit[i][m]=true;
  while (!q.empty())
  {
    temp=q.top();q.pop();x=temp.x;y=temp.y;
    for (i=0;i<=3;i++)
    {
      xx=x+dx[i];yy=y+dy[i];
      if (xx<1||xx>n||yy<1||yy>m||visit[xx][yy]) continue;
      visit[xx][yy]=true;
      if (a[xx][yy]>=temp.h) q.push((arr){xx,yy,a[xx][yy]});
      else ans+=temp.h-a[xx][yy],q.push((arr){xx,yy,temp.h});
    }
  }
  printf("%d",ans);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值