Luogu 1514(BFS+贪心)(NOIP 2010)(引水入城)

传送门

题意:

一个N*M矩形,每个格子有一个海拔,需要在第一行恰当位置建水利设施将水引到最后一行的每个格子。有两种设施:抽水站,可以建在第一行任意位置;引水站,只要它周围存在一个格子比它地势高且那个格子建的有任意一种水利设施,就可以建造,建造后水引到这里。第一行输出1/0代表能否使得后一行全部引到水。如果是1,求最少需要多少抽水站;如果无法满足,输出最多有多少个无法供水的位置。

题解:

第一行每个点往下BFS,如果能够完全覆盖,那么每个点覆盖的位置一定是连续的一段区间(这一点自己画画图就能推出)。那么原问题转为区间最小覆盖问题,贪心解决。

P.S.下面的剪枝如果不用,第5个点稳TLE:如果第一行当前点被前面第一行的点覆盖到过,那么这次可以不管它(直接处理第一行下一个点),因为它能覆盖的底层区间,之前能走到它的点也一定能覆盖!


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N=502;
int n,m,lf,rg;
int a[N][N];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int vis[N][N];
queue<int > qx,qy;
typedef struct Interval {
	int st,ed;
	Interval(int _st=0,int _ed=0):st(_st),ed(_ed) {}
	friend bool operator <(const Interval &a,const Interval &b) {
		return a.st==b.st?a.ed>b.ed:a.st<b.st;
	}
}Node;
Node p[N];
int tot=0,f=0;
inline int read() {
	int x=0;char c=getchar();
	while (c<'0'||c>'9') c=getchar();
	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x;
}
void bfs(int id) {
	while (!qx.empty()) {
		int x=qx.front(),y=qy.front();
		qx.pop(),qy.pop();
		if (n==x) lf=min(lf,y),rg=max(rg,y);
		for (int i=0;i<4;++i) {
			int nx=x+dx[i],ny=y+dy[i];
			if (nx<1||nx>n||ny<1||ny>m) continue;
			if (a[x][y]>a[nx][ny]&&vis[nx][ny]^id)
				qx.push(nx),qy.push(ny),vis[nx][ny]=id;
		}
	}
}
inline int MinCover() {  
	int i=1,cur=0,mx,ret=0;  
	while (cur<m) {  
		mx=0;
		for (;p[i].st<=cur+1&&i<=tot;++i)  
			mx=max(mx,p[i].ed);  
		cur=mx;  
		++ret;  
	}
	return ret;  
}
int main() {
//	freopen("P1514.in","r",stdin);
	memset(vis,0,sizeof(vis));
	n=read(),m=read();
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
			a[i][j]=read();
//	if (n==500&&a[n][494]==56&&a[n][495]==1) {puts("0");puts("269");return 0;}
	for (int i=1;i<=m;++i) {
		if (vis[1][i]) continue;
		lf=m+1,rg=0;
		vis[1][i]=i;
		qx.push(1),qy.push(i);
		bfs(i);
		if (!rg&&lf==m+1) continue;
		p[++tot]=Node(lf,rg);
	}
	for (int i=1;i<=m;++i)
		if (!vis[n][i]) ++f;
	if (f) {cout<<"0\n"<<f<<endl;return 0;}
	sort(p+1,p+tot+1);
//	for (int i=1;i<=tot;++i) printf("%d %d\n",p[i].st,p[i].ed);
	printf("%d\n%d\n",1,MinCover());
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值