【题解】「luoguP1514」引水入城

做题还得继续

下面讲一道复习型题目 先看题

说是要在湖边建水利设施 求能否到达最后一行并铺满 并求出设施数或无法到达区域数

说前三十分是无法铺满的

那么就输出样例可以使用floodfill 或者说用深广搜就可以拿到分数

比如这样

#1

搜完之后扫一下最后一行看有没有干的

下面给出BFS代码

const int posx[] = { 1,0,-1,0 };
const int posy[] = { 0,1,0,-1 };
#define GOX b+posx[i]
#define GOY a+posy[i]
//g是记录高度的数组
inline void bfs(int start)
{
	int a,b;
	q.push(1);
	q.push(start);
	flag[1][start] = true;
	if (n == 1)sit[start] = true;
	for (; !q.empty();)
	{
		a = q.front();
		q.pop();
		b = q.front();
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			if(GOX<=m&&GOX>0&&GOY<=n&&GOY>0)
				if (g[GOY][GOX] < g[a][b])
				{
					if (flag[GOY][GOX] == false)
					{
						flag[GOY][GOX] = true;
						q.push(GOY);
						q.push(GOX);
						if (GOY == n)
							sit[GOX] = true;
					}
				}
		}
	}
}

如果铺的满呢

这里我们来思考一下

经过搜索后我们能知道什么

很明显我们知道了第一行每一个水利设施能覆盖的区域

如果只关注最后一行就是知道每个设施能覆盖的“段”

如果这个“段”是“线段”就好了

那么 这个“段”是否连续呢?

那么我们来一个简短不严谨的的证明

要想铺满的话“段"是否必须是连续的

首先我们制造一个不连续的“段” 像这样

#2

或者这样

#3

想一想会发现 这一个无法到达的区域会被水包围(这个我想了很久)

别的水利设施有水也会被这一个“圈”挡住

#4

因此可知得知若能铺满,则设施所覆盖的区域必定为连续线段

因此便可以用贪心跑线段覆盖

解释一下

就是找能覆盖区间左端的线段中最长的 并将线段右端更新为区间左端 然后再去找

给出线段覆盖代码

for (; r > l;)
	{
		for (int i = 1; i <= m; i++)
		{
			if(l==1&&seg[i][0]==1)
				temp = max(temp, seg[i][1]);
			 else if (seg[i][0] <= l+1&&l!=1)
				temp = max(temp, seg[i][1]);
			
		}
		l = temp;
			successcnt++;
			temp = 0;
	}

最后提醒一句 测试点#6 只有一行 很狗血 我的代码加了特判

下面给出完整代码

注意一下clear函数

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int posx[] = { 1,0,-1,0 };
const int posy[] = { 0,1,0,-1 };
#define GOX b+posx[i]
#define GOY a+posy[i]
queue<int>q;
int g[505][505];
int n, m;
int failcnt,successcnt;
bool flag[505][505];
int seg[505][2];
bool sit[505];
inline void clear()
{
	for (; !q.empty();)
		q.pop();
	//这里没有清除第一行的flag 为什么? 
	//如果第一行的某个点被覆盖 则不需要用它起搜了 为什么? 
	for(int i=2;i<=n;i++)
	memset(flag[i], false, sizeof(flag[i]));
	if (n == 1)
		memset(flag, false, sizeof(flag));
}
inline void bfs(int start)
{
	int a,b;
	q.push(1);
	q.push(start);
	flag[1][start] = true;
	if (n == 1)sit[start] = true;
	for (; !q.empty();)
	{
		a = q.front();
		q.pop();
		b = q.front();
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			if(GOX<=m&&GOX>0&&GOY<=n&&GOY>0)
				if (g[GOY][GOX] < g[a][b])
				{
					if (flag[GOY][GOX] == false)
					{
						flag[GOY][GOX] = true;
						q.push(GOY);
						q.push(GOX);
						if (GOY == n)
							sit[GOX] = true;
					}
				}
		}
	}
}
int main()
{
	//freopen("in.txt", "r", stdin);
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			scanf("%d", &g[i][j]);
	for (int i = 1; i <= m; i++)
	{
		if (flag[1][i] == false)
		{
			clear();
			bfs(i);
			for (int j = 1; j <= m; j++)
				if (flag[n][j] == true)
				{
					seg[i][0] = j;
					break;
				}
			for (int j = m; j > 0; j--)
				if (flag[n][j] == true)
				{
					seg[i][1] = j;
					break;
				}
		}
	}
	for (int i = 1; i <= m; i++)
	{
		if (sit[i] == false)
			failcnt++;
	}
	if (failcnt != 0)
	{
		printf("0\n%d", failcnt);
		return 0;
	}
	int l, r;
	l = 1;
	r = m;
	/*for (int i = 1; i <= m; i++)
	{
		if (seg[i][0] == 1)
		{
			l = max(l, seg[i][1]);
			successcnt = 1;
		}
	}*/
	int temp=0;
	for (; r > l;)
	{
		for (int i = 1; i <= m; i++)
		{
			if(l==1&&seg[i][0]==1)
				temp = max(temp, seg[i][1]);
			 else if (seg[i][0] <= l+1&&l!=1)
				temp = max(temp, seg[i][1]);
			
		}
		l = temp;
			successcnt++;
			temp = 0;
	}
	printf("1\n%d", successcnt);
	return 0;
}

事实上,如果不用上述clear函数
而是改为

memset(flag,false,sizeof(flag));

分析可知其最大时间复杂度为500x500x500=125000000=1.25x10^8
而NOIP数据这么水卡常也是可以过的

谢谢观赏

posted @ 2020-11-02 07:17  LPYAX(a.k.aWYXAA)  阅读(55)  评论(1)    收藏  举报