引水入城-题解

蒟蒻改了很久,终于,写篇题解总结总结

1.大致思路

观察题面,我们可以总结出几个比较关键的要点

  • 水域会流动,并且需要有一定条件才能进行流动,可以想到用 b f s bfs bfs 去模拟水的流动。

  • 需要判断水流是否能灌溉干旱线,可以使用 b f s bfs bfs 来判断。

根据大致对题面的了解,我们大致可以知道第一问需要用搜索解决,第二问待会儿考虑。

2.BFS

因为我们先开始不知道需要几个输水站,所以最好的情况就是用所有的输水站输水,如果用上了全部的输水站还不行,那肯定就不可以了。关于如何详细的打,这里就不细讲了。

void bfs(int x,int y){
	queue<node>q;
	t1.x=x,t1.y=y;
	q.push(t1);
	while(!q.empty()){
		t1=q.front();
		q.pop();
		rep(i,0,3){
			int dx=t1.x+dir[i][0],dy=t1.y+dir[i][1];
			if(dx>=1 && dx<=n && dy>=1 && dy<=m && flag[dx][dy]==0){
				if(a[t1.x][t1.y]>a[dx][dy]){
					flag[dx][dy]=1;
					t2.x=dx;
					t2.y=dy;
					q.push(t2);	
				}
			}
		}
	}
}

3.第二问

  • 确定方法

此题需要使用区间覆盖来解决。(想不通看下面)

  • 确定区间

首先区间问题一类的需要区间,此题区间是什么呢?是每个输水站能输到的最左端和最右端。

  • 证明区间连续

大家可能会问,输水站所运到的区间可能是不连续的,下面我们来证明实际上如果可以灌溉所有的干旱线,那么每个输水站所能运到水的区间一定是连续的。

黄色的表示一个输水站所运输的水,最后一行就是他所运到的地方,我们假设他是不连续的。

这是另一个输水站的运输路线,因为需要让最后一行所有的都覆盖,所以必须要经过绿色的点,而我们发现,绿色是黄色部分与绿色部分相交的部分,那么就可以知道黄色部分必定是不连续的,与我们假设的相反。因此,得证:如果可以灌溉所有的干旱线,那么每个输水站所能运到水的区间一定是连续的。

  • 实现

已经证明完了,则可以使用区间覆盖来解决。

细节很多,需要细细品味。

	int ans=0,s=1,t=m,lastr=1;
	sort(qj,qj+1+m,cmp);
	rep(i,1,m){
		if(s>t) break;
		int p=-INF;
		rep(j,1,m){
			if(s<qj[j].l) break;
			p=max(p,qj[j].r);
		}
		ans++;
		s=p+1;
	}

c o d e code code

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath> 
#include<queue>
#define INF 0x7f7f7f7f
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define dep(i,r,l) for(int i=r;i>=l;i--)
using namespace std;
const int N=5e2+5;
const int M=5e2+5;
int n,m;
int a[N][M],flag[N][M],dir[4][2]={{1,0},{0,1},{0,-1},{-1,0}},Y;
struct node{
	int x,y;
}t1,t2;
struct node_dp{
	int l,r;
}qj[M];
bool cmp(node_dp x,node_dp y){
	if(x.l!=y.l) return x.l<y.l;
	return x.r>y.r;
}
void bfs(int x,int y){
	queue<node>q;
	t1.x=x,t1.y=y;
	q.push(t1);
	while(!q.empty()){
		t1=q.front();
		q.pop();
		rep(i,0,3){
			int dx=t1.x+dir[i][0],dy=t1.y+dir[i][1];
			if(dx>=1 && dx<=n && dy>=1 && dy<=m && flag[dx][dy]==0){
				if(a[t1.x][t1.y]>a[dx][dy]){
					flag[dx][dy]=1;
					t2.x=dx;
					t2.y=dy;
					q.push(t2);	
				}
			}
		}
	}
}
int main(){
	scanf("%d %d",&n,&m);
	rep(i,1,n)
		rep(j,1,m)
			scanf("%d",&a[i][j]);
	rep(i,1,m) flag[1][i]=1;
	rep(i,1,m) bfs(1,i);
	rep(i,1,m){
		if(flag[n][i]==0){
			Y=1;
			break;
		}
	}
	if(Y==1){
		puts("0");
		int ansN=0;
		rep(i,1,m) ansN+=abs(flag[n][i]-1);
		printf("%d",ansN);
	}
	else{
		puts("1");
		rep(i,1,m){
			memset(flag,0,sizeof(flag));
			flag[1][i]=1;
			bfs(1,i);
			rep(j,1,m){
				if(flag[n][j]==1){
					qj[i].l=j;
					break;
				}
			}
			dep(j,m,1){
				if(flag[n][j]==1){
					qj[i].r=j;
					break;
				}
			}
		}
		int ans=0,s=1,t=m,lastr=1;
		sort(qj,qj+1+m,cmp);
		rep(i,1,m){
			if(s>t) break;
			int p=-INF;
			rep(j,1,m){
				if(s<qj[j].l) break;
				p=max(p,qj[j].r);
			}
			ans++;
			s=p+1;
		}
		printf("%d",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值