【蓝桥杯】(试题集-算法提高)积水

积水【蓝桥杯 试题集 算法提高】

【90分】(我也不知道为什么 O(1e8)的算法 会T一个点…
题目如上
判断:数据范围n<=100,图中一共n^2 个点
暴力O(n^4)可行 ⇒ 关键在于如何暴力

核心思路:
形成 “谷地” 才能积水,而判断一个点是否在谷地的方法,即找到他周围的 “包围圈”
思路延申1:
当且仅当这个点无论向哪个方向走走到头,都会遇到比自己高的点时,这个点在包围圈内。 且遇到的比自己高的点即为该点的包围圈的成员
思路延申2:
存在包围圈套包围圈的情况,即如果随机一个点,对它的包围圈进行暴力寻找,遇到比自己高的就停,则有可能找到一个不够大的包围圈。
思路延申3:
基于1,2 。 对于一个谷地点 (x,y),它的积水应该是多少?
(x,y)点 可以向外拓展的 最大”的包围圈的高度 ans[x][y]。
其中包围圈的高度=min( 包围圈上各点的高度)。
该点的积水量为 ans[x][y] - height[x][y]

为避免情况2,我们做一次高度排序,从高到底进行包围圈判断
时间复杂度:
点数N<=10000
排序O( N log N ),
暴搜包围圈,遍历每个点是N级别,对每个点搜索包围圈的最差情况是将N个点全搜,所以 O( N^2 )

实操如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read(){
	char ch=getchar();bool f=true;int sum=0;
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=false;ch=getchar();}
	while(ch>='0'&&ch<='9'){
		sum*=10;sum+=ch-'0';ch=getchar();}
	if(f)return sum;else return -sum;
}
#if 0
Goes && G.S.M.
Title://积水 深搜 
#endif
const int N=105;
const int INF=(1<<30);
int h[N][N];
int ans[N][N];long long res;//被围住的高度 
int n,m;
//包围圈中最低的高度为包围圈高度
//对于每个点,这个点的存水量=ans[i][j]-h[i][j]
//其中ans[i][j]表示所有将该点包围的包围圈中,高度最高的包围圈高度
struct ss{
	int x,y,h;
}hh[N*N];
bool cmp(ss a,ss b){
	return a.h>b.h;
}//从高点往低点搜,可以保证更高包围圈优先被判断 

bool vis[N][N];
int dfs(int x,int y,int va){
	if(x<1||x>n||y<1||y>m) return 0;//漏水
	vis[x][y]=true;
	if(h[x][y]>va){//大于搜索起点的高度,则是包围圈 
		return max(h[x][y],ans[x][y]);//记忆化 剪枝 
		//更外围包围圈or已经是最外围 
	}
	
	int ls=INF;
	if(!vis[x+1][y]) ls=min(ls,dfs(x+1,y,va));

	if(!vis[x-1][y]) ls=min(ls,dfs(x-1,y,va));

	if(!vis[x][y+1]) ls=min(ls,dfs(x,y+1,va));
	
	if(!vis[x][y-1]) ls=min(ls,dfs(x,y-1,va));
	return ls;
} 
  
int main(){
	memset(ans,-1,sizeof(ans));
	n=read();m=read();int ecnt=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++){
		h[i][j]=read();
		hh[++ecnt]=(ss){i,j,h[i][j]};
	}
	sort(hh+1,hh+n*m+1,cmp);
	
	for(int i=1;i<=n*m;i++){
		memset(vis,false,sizeof(vis));
		int x=hh[i].x,y=hh[i].y,va=hh[i].h;
	//	if(ans[x][y]==-1)	
			ans[x][y]=dfs(x,y,h[x][y]);
		if(ans[x][y]>=h[x][y]) res+=ans[x][y]-h[x][y];
		else res+=0;//ans=0表示流出土地,ans<h表示i自己是边界 
	}

	cout<<res<<endl;
	return 0;	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoesM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值